函数
标准库函数
直接调用 #include<头文件>
自定义函数
返回值类型 函数名(形参列表){
函数体
}
返回值类型:
返回值的类型
一个函数可以没有返回值 声明为 void
return语句可以返回多个数据,
但函数调用接收时,只接收到一个 return 1,2;
返回值类型可以为指针(指针即内存地址)
但需要注意,不能返回局部变量的地址
函数名: 标识符
形参列表: 表明函数在需要的参数个数和类型
多个参数用,隔开
即使类型相同,类型关键字也不能省略
参数传递过程:值传递(复制)
实参和形参只是在函数调用时值相等 是不同的变量
void func(); 表明在调用时可以传递任意多的参数
void func(void); 表明在调用时不可以传递参数
return:
return [x]; 结束一个函数调用 返回结果
结束一个程序:
exit(status);
如果是在main函数用 return
main:是C语言程序的入口,一个C语言程序中有且只有一个main函数
有一种特殊的函数: 参数个数不固定
void func(const char *format, ...);
函数声明 和 定义:
函数声明:只是声明一个函数,没有函数体
函数定义: 一定是包含函数体(实现)
基本上 声明和定义是一起的
void func(int);//声明 形参名可省,只保留形参类型
void func(int a){//定义
}
隐式声明:
在编译C语言代码中,如果遇到一个调用的函数
在此之前没有被声明过,那么编译器会隐式声明一个
同名的函数,并且返回值类型默认为int
C函数默认返回值类型为int,所以为int时可省
extern 可以声明外部定义的函数
数组
是同类型的多个变量
数组中的元素是同类型的
一维数组 二维数组 三维数组 语法支持n维数组
定义:
类型 数组名[数组长度];
int drr[5]={};//全部0
int err[5]={1,2,3,4,5,6};//警告
int frr[5]={[2]=3,[4]=1};
int grr[] = {1,2,3,4};//由值决定数组长度 一定需要初始化
int hrr[];//错误的
二维数组:
数组长度 = sizeof(数组名)/sizeof(数组元素)
数组作为参数传递:
int arr[10] = {};
func(arr,10);//需要传递数组长度
字符数组
char str[10] = "Hello";
reverse(str);//不需要传递字符串长度
二维数组: 有多少个一维数组
int arr[10][5] = {};//有10个长度为5的一维数组
func(arr,10); bar(arr,10);
void func(int arr[][5],int len);
void bar(int (*arr)[5],int len);
下标运算符[]
arr[n] == *(arr+n) == *(n+arr) == n[arr]
int *p;//一级指针 指针指向一个int类型的数据
int *arr[5];//指针数组 本质是数组 数组中的元素是指针
int crr[5];//数组
crr == &crr[0] == &(*(crr+0)) == &*crr
数组名: 数组首元素的地址
&crr //整个数组的地址
int arr[10];
arr == &arr[0]
&arr == 数组指针 int (*parr)[10];
int brr[10][5];
二维数组名 是一个 数组指针 这个指针指向一个一维数组
brr == &brr[0] == &*(brr+0) == &*brr
brr[0] == &brr[0][0] == &*(brr[0]+0)
brr[0] 相当于二维数组中第一个元素的地址 int*
brr = &brr[0] == int (*p)[5];
指针
指针即内存地址
内存地址从0开始编号
一个程序有4G的内存地址[0,0Xffff ffff]
保存内存地址只需要4个字节
sizeof(指针) == 4 (有的系统为8)
定义指针变量:
类型 *指针变量;
类型 是指指针所指向的那片内存区域中数据的类型
(类型 *) 指针的类型
指针的运算:
& 取址
* 取值
+ 整数 +1相当于加一个单位长度的内存
char *p;p+1 1个字节
int *pi; pi+1; 4字节
double *pd;pd+1; 8字节
int (*pa)[5]; pa+1;20
double (*pb)[2][5];pa+1; 80
char (*pc0)[10][5]; pc0+1; 50
char *(*pc1)[10][5];pc1+1; 200=10*5*4
char **pc2[10][5]; pc2+1; 20=5*4
char *pc3[10][5]; pc3+1; 20
int arr[5]; arr+1; 4
void 作为返回值类型 作为形能列表
空指针NULL
万能指针 void *
任意类型的指针都可以隐式转换为void*任何
该指针丢失了类型限定
万能指针不能取*运算
虽然不能进行取*运算,但非常有用
字符串
一串字符并能’\0’为结束标识称为字符串
字符串的长度,通过字符串首地址能求得
字符串有结束符’\0’
形式:
1.字面值字符串
“Hello”
特点: (1)不改修改
(2)存储代码区
(3)字面值相同的字符串在内存中共享一份
(4)连续的字面值字符串自动合并
“Hello”“World”
2.字符数组
char str[5] = {‘H’,‘e’,‘l’,‘l’,‘o’};
如果只是单纯作为字符数组没问题
但是如果作为字符串,有问题
char s[] = “Hello”;//s在栈区
需要有足够的内存保存’\0’
特点:(1)存储在栈区
(2) 可以修改
3.字符指针
对于字符串而言,只需要记录字符串开始地址
char *p = “Hello”;// *p取到是’H’
//*p = ‘e’ 不可以
char s[] = “Hello”;
char *ps = s;// *ps ‘H’
*ps = ‘E’;//可以的
数组指针
本质是指针,指针指向一个数组
int (*arr)[5];
指针数组
本质是数组,数组中的元素是指针
int *arr[5];
函数指针: 在C语言中的应用很广泛
本质是指针,指针指向一个函数
int (*func)();
func是一个函数指针,本质是指针
指针函数
本质是函数,函数返回值类型是指针
int *func();
其它的字符串函数:
scanf() 以空白字符为分隔 读到的字符串不能有空白字符
char *gets(char *s);//C语言不建议使用 c11删除
一直读到’\n’为止 不会对容量进行检查 非常危险
char *fgets(char *s,size_t n,FILE *stream);
从stream中读取n个字符到s中,能够确保有’\0’
最多能读到n-1个字符 ,如果不到n-1字符,会连’\n’也一并读取
需要手动处理’\n’
FILE *stream: stdin
stdin: 标准输入
stdout: 标准输出
stderr: 标准错误输出
int puts(const char *s);//输出字符串到控制台
int fputs(const char *s,FILE *stream);//输出字符串到stream
getc/fgetc
putc/fputc
标准输入输出函数:
参数不定 >=1
printf
int printf(const char *format,…);
把内容输出到输出缓冲区
数据什么时候会输出到终端控制台呢?
1.遇到\n
2.强制刷新输出缓冲区 fflush(stdout)
3.当输出缓冲区满了 4kB
int scanf(const char *format,...);
从输入缓存区中格式读取内容,如果输入缓存区中没有内容
则等待用户输入
如果输入缓存区中有内容,不会让用户输入
用户输入内容时,遇到Enter,
会把用户输入的内容全部写入到输入缓存区中
输入缓存区:3.14\n
%d 3 .14\n
%lf 0.14 \n
%c \n 不会跳过空白字符
//清空输入缓存区
scanf("%*[^\n]");//读取缓存区中的内容(不是\n)舍弃
scanf("%*c");//读取任意一个字符舍弃
格式占位符
"%-m.nf"
m内容所占宽度
n小数所占宽度
+ 右对齐 -左对齐
不定长参数列表:
void func(int x, …);
在…之前一定要有一个有名参数 但不限类型
第一个参数,是为了知道在调用该函数时,后面传递了多少个实参
printf() scanf() format中有格式占位符
#include <stdarg.h>
void va_start(va_list ap,last);
type va_arg(va_list ap,type);
void va_end(va_list ap);
取出 ... 的数据:
1.定义一个 va_list 变量
va_list ap;
2.初始化ap变量
va_start(ap,last); //last是有名参数的最后一个参数的名字
3.每调用一次能从不定长列表中取出一个数据
如果每一个数据的类型不一样,还得考虑当前是什么类型
例如printf scanf通过格式占位符来获取类型信息
type va_arg(ap,type);
type 是类型
4.释放资源
va_end(ap);
const char *s;
const 表示只读
不可以修改(并不是一定不能修改)
const char * p1; //const修饰*p1 p1只读
char const * p2; //const修饰p2 *p2只读
char * const p3; //const修饰 p3 p3只读
const char * const p4; // *p4 和 p4都是只读