四. 函数的参数
1. 实际参数(实参)
实参为传递给函数的参数,它可以是常量,表达式,函数(这里实参作为另一个函数的返回值)等。在实参传递时,他必须要有确定的值以便于形参接受。
2.形式参数(形参)
形参是实参的一份临时拷贝,当一个函数被调用时,形参才被实例化,当函数结束时,形参也相继被释放。
五. 函数的调用
1.传值调用
例如:求两个数的最大值
#include <stdio.h> int Max(int a, int b) { return (a > b ? a : b); } int main() { int a = 0; int b = 0; scanf("%d %d", &a, &b); int ret = Max(a, b); 这里传递的是 a b 的值 printf("MAX = %d", ret); return 0; }
运行结果:
2. 传址调用
当我们调用函数想要改变实参的值,这是我们应该使用传址调用,因为形参接受实参值时会开辟另外的空间来存放,这时想要通过改变形参来达到改变实参的目的,就不会实现,因为地址不同,所以我们要传地址。
例如:交换两个整型值
#include <stdio.h> void ecg(int* a, int* b) { int tmp = *a; *a = *b; *b = tmp; } int main() { int a = 0; int b = 0; scanf("%d %d", &a, &b); printf("交换前:%d %d\n", a, b); ecg(&a, &b); printf("交换后:%d %d\n", a, b); return 0; }
运行结果:
六. 函数的嵌套调用和链式访问
1.函数的嵌套调用
这里用代码来展现:
#include <stdio.h> int ADD(int c) { return 2 * c; } int doubleAdd(int a, int b) { int c = a + b; int rets = ADD(c); 调用 2*实参 的函数 return rets; } int main() { int a = 0; int b = 0; scanf("%d %d", &a, &b); int ret = doubleAdd(a, b); 调用函数 printf("%d\n", ret); return 0; }
2. 函数的链式访问
把一个函数的返回值作为另一个函数的参数
这里用一段典型代码来表示:
#include <stdio.h> int main() { printf("%d", printf("%d", printf("%d", 43))); return 0; }
补充:printf函数返回值是一个数的位数 ,也就是有几个数。
七. 函数的声明和定义
1. 函数的声明
(1)在一个项目中声明一般在头文件里,定义与使用一般在不同的 .C 文件中。
(2)函数的使用一定要先定义后声明后使用,如果一个自定义函数模块放在main函数后面,那么在main函数前一定要声明,不然当你在main函数中使用定义的函数时,编译器从上至下编译你的代码不会先编译你的函数定义的内容,这时就会报某某未定义错误。
(3)声明就是告诉编译器函数叫什么,参数是什么,返回类型是什么。
2. 函数的定义
定义就是函数功能的实现,使它能够完成项目的某个模块。
前面有定义的例子,这里就不举例了。
八. 函数的递归
1. 什么是递归?
递归实际上就是程序自己调用自己,它常常可以把一个复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,这可以大大减少代码量,所以递归的主要思考方式在于:把大事化小。
2. 递归必要的两个条件
1.存在限制条件,当满足这个限制条件后,递归便不再进行。
2.每次递归第调用都要越来越接近这个限制条件。
3. 递归练习
1.求n的阶乘
#include <stdio.h> int jc(int n) { int i = 0; if (n <= 1) { return 1; } else { return n * jc(n - 1); } } //递归 int main() { int n = 0; scanf("%d", &n); int ret = jc(n); printf("%d", ret); return 0; }
2.正序打印一个整型值的每一位
#include <stdio.h> void Print(int n) { if (n > 0) { Print(n / 10); } else { return 0; } printf("%d ", n % 10); } int main() { int n = 0; scanf("%d", &n); Print(n);// 正序打印一个整数的每一位 return 0; }
3. 递归实现strlen求字符串长度
#include <stdio.h> int my_strlen(char* str) { if (*str != '\0') { return 1 + my_strlen(str + 1); } else { return 0; } } int main() { char arr[] = "hello"; int len = my_strlen(arr); printf("%d", len); return 0; }
4. 递归的局限性
值得注意的是:因为递归有以上良好的功能,所以其思考难度较大。
有时,递归使代码量大大减少的同时也增加了程序的运行难度(运行效率大大降低,如:求第n个斐波那契数),并且其可读性没有非递归那么好,所以递归也要好好思考来选择。
如果递归使用不当,就会出现栈溢出(“Stack overflow”)现象,这是因为每次函数调用都会在内存的栈区开辟一个自己的空间,如果递归过多导致内存栈空间开辟过多,这时就会出现溢出现象。
九. 总结
总的来说,函数在C语言中具有相当高的地位,它的存在使得一个项目更具模块化,便捷化,实效性和可读性,因此,我们在学习C语言当中,应养成函数定义这一习惯,并将使用函数的思维融会贯通。