目录
7.1 函数的基本概念和定义
**作用:**函数将一段可复用的代码封装,减少重复性的代码
函数说明
- 一个文件中包含一至多个函数,这个文件被称为源文件,在大型项目中,一个项目由多个源文件组成,不同的函数放在不同的源文件中
- C程序从main函数开始执行,最终也是在main函数中结束整个程序的执行,并且main函数不能被自定义的函数调用,main函数是由系统调用的
- 函数一般分为库函数和自定义函数两类,比如 printf 函数就属于库函数
函数的定义和返回值
函数参数:调用函数时,我们需要一些变量来接受传入的数据,这些变量就是函数参数
形式:
返回类型 函数名 (形式参数列表)
{
一条或多条语句
return 返回值;
}
- 无返回类型无形参
void foo()
{
printf("这是一个无返回类型无形参函数");
return ; // 这一句也可以不写,或者写成返回空
}
- 有返回值有形参
int add(int a, int b)
{
return a + b;
}
int main()
{
int res = add(3, 4);
return 0;
}
说明:
- 调用函数时,会为形参分配空间,但是函数执行完之后,该内存就会被释放,所以形参只能在函数内部使用
- 上述例子中,3 和 4 为实际参数,而 a 和 b 为形式参数,实参的值传给形参是单向传递,即只能由实参传递值给形参,不能由形参传递值给实参
int ValueChange(int a)
{
return a + 10;
}
int main()
{
int a = 10;
int res = ValueChange(a);
printf("%d\n", a); // 输出:10
printf("%d\n", res); // 输出:20
}
7.2 函数的调用方法和嵌套调用
// 函数定义
void printfHello()
{
printf("Hello");
}
// 函数声明
void printfHello();
// 函数调用
printfHello();
可以看出,函数的定义是最全的,函数声明必须要给出全部的接口信息,函数调用则只需要给出名字和传入数据即可
Tips: 一般在实际工程中,可以将所有的自定义函数的函数声明写在一个头文件中,到时候在每个源代码文件的开头中,使用 #include 语句将这个头文件包含进来就好
函数的嵌套调用
在不少语言中,允许在一个函数中写另一个函数的定义,但是在C语言和C++语言中,只能在一个函数中调用一个函数,不能直接定义一个函数。
void foo1()
{
printf("这是函数 1");
}
void foo2()
{
foo1(); // 输出:这是
printf("这是函数 2");
}
7.3 函数的递归调用
**本质:**递归就是自己调用自己,递归看似代码很短,但其实是一个笨方法,好处是可以根据上一步来求索当前的答案,对于某一些问题有很大的作用
**注意:**调用栈的内存是有限的,因此必须要有递归终止的条件

int dg_jiecheng(int n)
{
int result;
if (n == 1) // 终止条件
retult = 1;
else
retult = dg_jiecheng(n - 1) * n;
return result;
}
7.4 数组作为函数的参数
数组元素作为函数的参数
只要将数组元素看成是一个变量,以变量的形式传就好了
数组名作为函数的参数
- 一维数组的话,唯一的注意点就是函数的对应形参要写成数组名或者数组指针,并且由于传递的是地址,那么这种传递方式是地址传递,形参的改变会导致实参的改变
- 多维数组的话,需要注意形参可以省略一维的数量,但是不能省略二维或者多维的数量,一般我们建议,最好保持和作为实参的数组一张就好
void changeValue(int ba[5][8]); // 函数声明
void changeValue(int ba[5][8]) // 函数定义
{
ba[0][2] = 15;
return ;
}
int main()
{
int a[5][8];
a[0][2] = 12;
changeValue(a);
return 0;
}
7.5 局部变量和全局变量
局部变量
**定义:**在一个函数内部定义的变量叫做局部变量,并且,局部变量只有在本函数内CIA可以使用
- mian 函数是主函数,也是一种函数之一,因此,main 里面定义的变量也是只能main函数中使用,否则的话,函数调用的话就不需要传递数据了
- 不同函数的内部可以使用相同的变量名,互不干扰
- 形参也是局部变量
- 局部变量的一大特性是未使用该函数前,该变量是不占空间的,只有使用该函数时才会临时分配空间
- 有一种特殊写法——复合语句(=={} 号的特殊用处
int main()
{
int a, b;
......
{
int c; // 变量 c 的有效范围在 {} 之内
c = a + b;
}
}
// 从上述例子也可以看出,{} 具有区域自治的感觉,因此,一个函数内的多个 for 循环可以都使用 i 作为变量
全局变量
定义: 在函数体外定义的变量就称为全局变量
特性:
- 全局变量可以被该源文件的所有函数引用(前提加入外部变量说明),而且,只要是在同一个项目中,全局变量可以被另外的源文件所使用
- 全局变量在一开始就分配了内存,直到 main 函数结束该内存才被释放,因此需要谨慎使用
- 全局变量的存在使得一些依赖全局变量的代码没办法直接移植到别的文件中,使得代码可移植性变差
extern 关键字
用途 1:当某一全局变量在该函数的后面,而该函数又想使用这个全局变量时
函数体外部进行外部变量声明
extern c1, c2; // 外部变量声明,再次强调,不可以在声明时初始化
void lookValue()
{
c1 = 5; // 声明后就这个函数就可以使用在函数该函数后面定义的全局变量了
c2 = 8;
return ;
}
int c1, c2; // 全局变量定义
int main()
{
lookValue();
printf("%d\n", c1);
printf("%d\n", c2);
return 0;
}
在函数体内进行外部变量声明
void lookValue1()
{
extern int c1, c2; // 外部变量说明
c1 = 5;
c2 = 8;
return ;
}
void lookValue2()
{
extern int c1, c2; // 外部变量说明
c1 = 51;
c2 = 81;
return ;
}
int c1, c2; // 全局变量定义
int main()
{
lookValue1(); // 这时候已经获取了 c1 和 c2 的值
printf("c1 = %d\n", c1);
printf("c2 = %d\n", c2);
lookValue1(); // 这时候已经获取了 c1 和 c2 的值
printf("c1 = %d\n", c1);
printf("c2 = %d\n", c2);
}
用途 2:
在另一个源文件中,只要在文件开头用 extern 关键字声明,就可以使用另一个源文件的全局变量了
全局变量和局部变量的关系
当在一个函数中时,有全局变量和局部变量同名时,在局部变量的作用范围内,优先使用局部变量
7.6 变量的存储和引用与内部和外部函数
变量的存储类别
**分类:**局部变量和全局变量是作用域的角度对变量进行分类。我们也可以从生存期对变量进行分类。
程序在内存中可以粗略分为:
程序代码区 |
---|
静态存储区 |
动态存储区 |
程序代码区:就是存储写的代码
静态存储区:存储全局变量、静态变量等,特点是程序开始时就分配固定内存,程序结束时才回收
动态存储区:存储局部变量、形参、返回地址等,函数被调用时才开始动态分配内存,结束后系统自动回收
局部变量的存储方式
一般来说,由于局部变量是存储在动态变量中,因此函数结束后会自动回收。如果想保留上次函数运行结束后的值,则需要将该变量变成静态局部变量
void test()
{
int c = 4;
printf("c = %d\n", c);
c ++;
return ;
}
int main()
{
test();
test();
test();
test(); // 最后会输出 4 行 c = 4
return 0;
}
而如果将 int c = 4 改为 static int c = 4
那么最后会输出 4 5 6 7
原因是:
局部静态变量是在编译时赋初值的,因此后面几次执行的话,是不会再赋初值了,而是直接保留上次运行结束后保留的值
全局变量的跨文件使用
当我们在 Visual Studio 2019 中创建了一个源文件 test 1,我们可以 ”我的文件夹“ 进入该文件夹内部,在同级中创立另一个源文件 test 2.然后在Visual Studio 2019 中点击添加,然后现有项,将 test 2 添加进 test 1 所属的虚拟父文件夹中
- extern :
当我们在 test 2 中创建了一个全局变量,在 test 1 中想要使用这个时,就需要用 extern 做外部变量声明, 最好是在文件的开头(因为哪行使用了 extern关键字,哪行后面的代码才使用该变量)
- static:
如果我们不想其他文使用这个全局变量,就可以用 static 将这个变量变成 静态全局变量,那么,别的文件就不能用 extern 使用,只能在自己这个源文件使用。而且,如果两个源文件的某一个全局变量名字是一样的,那么,加入 static 就可以使得两者共存而不报错