文章目录
1.函数定义
①.无返回值的函数:
函数头[返回类型] 函数名(参数表)
void sum(int begin,int end){函数体}
不能使用带值的return,可以没有return。
②.有返回值的函数:
int max(int a,int b){函数体}
一个函数中可以有多个return。
在函数中设置单一出口更好。
2.函数先后关系
C语言的编译器自上而下的顺序分析你的代码。
如果把要调用的函数放在主函数下面则会报错。
3.函数原型
函数头,以分号“;”结尾,构成了函数的原型。
其目的是告诉编译器这个函数的样子:
名称、参数、返回类型
4.参数传递
可以传递:字面量、变量、函数的返回值、计算的结果
若是类型不匹配?
调用函数时给的值与参数类型不匹配是C语言传统上最大的漏洞;
编译器会自动帮你把类型转换好,但可能不是你所期望的;
后续的语言,c++/java在这方面很严格。
C语言在调用函数时,永远只能传值给函数。
5.函数的参数和变量
5.1 本地变量
①.函数的每次运行就产生一个独立的变量空间,在则会个空间中的变量,
是函数的这次运行所独有的,称作本地变量。
②.定义在函数内部的变量就是本地变量。
③.参数也是本地变量。
生存期:什么时候这个变量开始出现,到啥时候它消亡了
作用域:在那个范围内可以访问到它(可以起作用)
对于本地变量,这个两个问题答案统一:在打括号(块)内。
本地变量的规则:
本地变量是定义在块内的;
程序进入这个块前其中变量不存在,离开后变量消失;
块外面定义的变量在块内仍然存在;
在块内的同名变量会将块外的覆盖使用;
不能在同一块内定义同名变量;
本地变量不会被默认初始化;
参数进入函数是会被初始化。
5.2 函数庶事
void f(void); 与 void f();
在传统C中后面的表参数未知,而不是没有参数。
C语言不可以函数嵌套定义;
int main()也是一个函数;
也可以写成int main(void);
return的0有人看:
Windows: if errorlevel 1 …
Unix Bash: echo $?
Csh: echo $status
6.数组
定义数组:
<类型> 变量名称[元素数量];
int number[100];
double weight[20];
元素数量必须是整数
c99之前:元素数量必须是编译时的字面量
c99之后:元素数量可以动态输入
数组的特点:
其中所有元素具有相同的数据类型
一旦创建,不能改变大小
数组中元素在内存中连续依次排列
使用数组需要初始化
数组中的单元:
赋值给数组: number[num] = x;
索引下标: grades[0];
长度为0的数组可以创建,但是没有用处
int a[0];
7.数组运算
7.1 数组的初始化
集成初始化:
int count[] = {1,2,3,4,5,6,7,8,9,10,14,18,};
另一种写法:
int count[num] = {0};
数组长度为num,且内容都为0。
C99独有写法:
int count[10] = { [4]=2,3,[8]=13 };
给指定位置赋值,没有定位的数据接在前面的位置。
(即3是索引为5的数据),其余位置的值补零。
特别适合初始数据稀疏的数组。
7.2 数组的大小
int a[] = {1,2,3,4,};
printf("%d \n", sizeof(a)); //16
printf("%d \n", sizeof(a[0])); //4
printf("%d \n", sizeof(a)/sizeof(a[0])); //4 //推荐使用
数组不可以这样赋值
int b[] = a; //错误写法
8.二维数组
int a[3][6]
通常理解为a是一个3行6列的矩阵
8.1 二维数组的遍历
for(int i=0; i<3; i++){
for(int j=0, j<5; j++){
a[i][j] = i*j;
}
}
8.2 二维数组的初始化
int a[][4] = {
{0,1,2,3},
{5,6,7,8},
};
* 列数必须给出,行数可以省略,由编译器来数
* 每行一个{},逗号分隔
* 如果省略元素,表示补零
9.指针
指针的&和*运算符
& 作用:获取变量的地址
* 作用:得到地址对应的值
保存指针的变量:int *p = &i;
指针作参数
通过&符号获取地址放入参数:
int i=0; fun(&i);
通过*符号接收指针:
void fun(int* p);
在函数内部可以通过这个指针访问外面。
数组变量是特殊的指针。
常见错误
定义了指针变量,还没有指向任何变量,就开始使用。
error eg: int *p; *p=12;
10.指针和const
指针的const的情况:
表示一旦得到某个变量的地址,则不能再指向其他变量。
int* const q = &i; //q是const
*q = 19; //ok
q++; //Error
所指是const的情况:
表示不能通过指针修改那个变量(不能使那个变量成为const)。
const int *p = &i; //(*p)是const
*p = 19; //Error
i = 19; //ok
p = &j; //ok
const数组保护元素:
const int a[] = {1,2,3,5,};
数组变量是const的指针,每个单元也都是const int,必须通过初始化赋值。
因为把数组传入函数时是地址,所以那个函数内部可以修改数组的值,可以设置const保护数组:
int sum(const int a[], int length);
11.指针运算
*p++ :
++的优先级比*高。 常用于数组的连续空间操作。
取出p所指的数据来,完事后顺便把p移到下一个位置去。
char ac[] = {0,1,2,3,4,5,6,7,8,9,};
char *p = &ac[0];
while(*p != -1){
printf("%d\n", *p++);
}
for(int i=0; i<sizeof(ac)/sizeof(ac[0]); i++){
printf("%d\n", ac[i]);
}
可以比较它们在内存中的地址;
数组中的单元的地址肯定是线性增加的;
<、<=、==、>、>=、!= 都可以对指针做;
0地址:
0地址存在,但通常是不能随便碰的地址,所以你的指针不应该有0值;
可以用0地址来表示特殊的事情:
* 返回的指针无效的; * 指针没有真正初始化(先初始化为0);
NULL是一个预定定义的符号,表示0地址。
指针类型及强制转换
指向不同类型的指针是不能直接互相赋值的,避免用错指针;
强制转换:int *p=&i; void* q=(void*) p;
void*表示不知道指向什么东西的指针;
这并没有改变p所指的变量类型,而是用不同的眼光通过p看它所指的变量。
指针动态内存分配
#include <stdlib.h>
Format : void* malloc(size_t size);
Example : (int*)malloc(n*sizeof(int));
申请空间的大小以字节为单位,需要转换成想要的类型;
如果申请失败则返回0或者NULL。
free()
申请得来的空间需要还给系统,只能还申请来的空间的首地址。