函数调用过程中通常伴随着两个函数之间的数据传递.数据传递存在两个完全相反的方向(可以从调用函数,向被调用函数传递数据也可以从被调用函数向,调用函数传递数据),任意方向的数据传递都使用被调用函数提供的存储区
/*
* 返回值练习
* */
#include <stdio.h>
int yue(void) {
int min = 0, max = 0, tmp = 0;
printf("请输入两个数字:");
scanf("%d%d", &max, &min);
while (max % min) {
tmp = max % min;
max = min;
min = tmp;
}
return min;
}
int main() {
int num = yue();
printf("最大公约数是%d\n", num);
return 0;
}
只能从被调用函数向调用函数传递一个数字,这个数字叫做被调用函数的返回值,只能在被调用函数结束的时候才能传递返回值.返回值需要记录在被调用函数提供的一个存储区里,需要把这个存储区的类型名称写在函数名称前,被调用函数里使用return关键字把返回值记录到这个存储区里.调用函数里把函数调用语句当作数字使用就可以得到函数的返回值
如果被调用函数里没有使用return关键字指定返回值,则函数的返回值是随机的.调用函数只有一次获得返回值的机会,得到返回值以后必须立刻使用或者存储到其他存储区里.如果函数不提供用来存放返回值的存储区就应该在函数名称前写void.如果函数名称前什么都没写在c89规范里表示函数提供一个整数类型存储区用来存放返回值,在C99规范里不允许这样
被调用函数不可以使用数组记录返回值
可以从调用函数向被调用函数传递多个数据,这些数据的类型可以不同,被调用函数需要为每个数据提供一个对应的存储区,在函数名称后的小括号里声明一组变量,这些变量用来代表前面提到的存储区,这些变量叫做函数的形式参数,小括号里的所有内容叫做形式参数列表,每个形式参数的类型名称都不可以省略,相邻形式参数声明之间用逗号分开,被调用函数里可以象使用变量一样使用形式参数
使用带参数的函数时需要在函数调用语句的小括号里为每个形式参数提供一个对应的数字。计算机把这些数字记录到对应的形式参数存储区里,被调用函数可以通过形式参数得到这些数字.这些数字叫做实际参数.能当作数字使用的内容都可以作为实际参数使用,如果函数不提供形式参数就应该在小括号里写void,如果函数名称后的小括号里什么都没写表示函数可以提供任意多个不确定类型的形式参数
/*
* 参数演示
* */
#include <stdio.h>
void print(int num, int num1) {
printf("%d\n", num);
printf("%d\n", num1);
}
int main() {
print(56, 30 - 7);
return 0;
}
数组可以作为形式参数使用.数组做形式参数的时候真正的形式参数并不是数组,只是把形式参数写成数组的样子.数组形式参数里包含的所有存储区都不是被调用函数提供的.声明数组形式参数的时候可以省略数组里包含的存储区个数.数组做形式参数的时候需要另外配合一个整数类型
参数用来表示数组里的存储区个数,通过数组形式参数可以让被调用函数使用其他函数的存储区.数组形式参数可以实现双向数据传递,这种参数叫输入输出参数
/*
* 数组形式参数演示
* */
#include <stdio.h>
void print(int arr[], int size) {
int num = 0;
for (num = 0;num <= size - 1;num++) {
printf("%d ", arr[num]);
}
printf("\n");
}
int main() {
int arr[] = {1, 2, 3, 4, 5};
print(arr, 5);
return 0;
}
C语言里函数参数的个数可以不固定这种参数叫变长参数.不可以在编写函数的时候对变长参数进行命名,在被调用函数里需要使用特殊的方法才能获得变长参数的内容
/*
* 函数练习
* */
#include <stdio.h>
void neg(int arr[], int size) {
int num = 0;
for (num = 0;num <= size - 1;num++) {
arr[num] = 0 - arr[num];
}
}
int main() {
int arr[] = {1, 2, 3, 4, 5}, num = 0;
neg(arr, 5);
for (num = 0;num <= 4;num++) {
printf("%d ", arr[num]);
}
printf("\n");
return 0;
}
如果编译器在编译的时候首先遇到函数调用语句就会猜测函数的格式。编译器认为函数有一个整数类型的存储区用来存放返回值,函数可以有任意多个不确定类型的形式参数。这个猜测结果叫做函数的隐式声明,隐式声明里参数的类型只能是整数类型或双精度浮点类型.如果隐式声明的格式和函数的真实格式不一致编译就会出错.函数大括号前面的部分可以单独写成一条语句,这种语句叫做函数声明语句.函数声明语句里可以省略形式参数名称,把函数声明语句写在文件开头叫做函数的显式声明,显式声明可以避免隐式声明的问题,除了主函数以外的所有函数都应该进行显式声明
exit标准函数可以随时结束整个程序的执行,为了使用这个标准函数需要包含stdlib.h头文件,这个函数需要一个整数作为实际参数使用,这个整数的作用和主函数返回值的作用一样
/*
* exit标准函数演示
* */
#include <stdio.h>
#include <stdlib.h>
void func(void) {
printf("2\n");
exit(0);
printf("3\n");
}
int main() {
printf("1\n");
func();
printf("4\n");
return 0;
}
C语言里函数可以调用自己,这种函数叫做递归函数
/*
* 递归函数演示
* */
#include <stdio.h>
void print(int max) {
if (max == 1) {
printf("1 ");
return ;
}
print(max - 1);
printf("%d ", max);
}
int main() {
int max = 0;
printf("请输入一个数字:");
scanf("%d", &max);
print(max);
printf("\n");
return 0;
}
如果一个问题可以分解成多个小问题,至少一个小问题和原来的问题本质上一样,只是稍微简单一点。这种问题就适合采用递归函数解决。
递归函数编写步骤
1.编写语句解决分解后的每个小问题(假设递归函数已经可以使用)
2.在递归函数的开头编写分支负责处理不可分解的情况(这个分支必须保证函数可以结束)
采用递归函数解决问题的思路叫递归,采用循环解决同样问题的思路叫递推
检验递归函数的时候首先用最简单的参数检验,然后逐渐把参数变得复杂再检验,如果所有参数的检验结果都正确就说明函数编写正确
能够使用某个变量的所有语句叫做这个变量的作用域,声明在函数里面的变量叫局部变量,局部变量的作用域包含函数里面的所有语句,声明在所有函数外边的变量叫全局变量,全局变量的作用域包含程序里的所有语句.没有初始化的全局变量自动被初始化成0,全局变量和局部变量可以重名,这个变量名优先代表局部变量,如果全局变量和局部变量都能解决问题应该优先采用局部变量
存储区的使用不受作用域限制(可以跨函数使用存储区),存储区的使用受到生命周期的限制生命周期是一段时间,在生命周期开始的时候计算机给程序分配存储区,在生命周期结束的时候,计算机把分配给程序的存储区收回,全局变量的生命周期是整个程序的执行时间,局部变量的生命周期是函数某一次执行的时间范围,在函数开始执行的时候计算机给局部变量分配存储区,在函数结束的时候计算机把局部变量的存储区收回.每次函数执行的时候局部变量被分配的存储区都可能是新的
静态变量的生命周期和作用域跟普通变量不一样,声明静态变量的时候需要使用static关键字.不论全局变量还是局部变量都可以声明成静态变量.静态局部变量的生命周期是整个程序的执行时间,没有初始化的静态变量自动被初始化成0.静态局部变量的存储区可以被任何函数使用,静态局部变量的作用域也是函数内部的所有语句,静态变量的初始化只在程序开始的时候执行一次,静态全局变量的生命周期还是整个程序执行时间,静态全局变量的作用域被压缩成只包含声明它的文件里的所有语句(不可以跨文件使用静态全局变量)
变量的作用域分为三种情况:局部变量,静态全局变量和非静态全局变量.变量的生命周期分为两种情况:非静态局部变量和其它