一、函数的调用
1. 函数调用的基本形式
返回值变量 = 函数名(实参列表); // 对有返回值函数 函数名(实参列表); // 对无返回值函数
2. 调用方式
-
单独调用:
printf("Hello");
-
表达式调用:
int sum = add(3,5) * 2;
-
参数调用:
printf("%d", add(2,3));
3. 调用过程
-
为形参分配内存
-
将实参值传递给形参(值传递)
-
执行函数体
-
返回返回值(如果有)
-
释放形参内存
二、函数的声明
1. 函数原型
返回类型 函数名(参数类型列表);
2. 声明的作用
-
告诉编译器函数的存在
-
确保函数调用时参数类型和数量正确
-
通常在头文件或文件开头声明
3. 声明示例
// 声明函数 double calculate(int a, double b); int main() { double result = calculate(5, 3.14); // 调用 return 0; } // 定义函数 double calculate(int a, double b) { return a * b; }
三、函数的嵌套调用
1. 概念
在一个函数内部调用另一个函数
2. 示例:计算组合数C(m,n)
int factorial(int n) { if(n <= 1) return 1; return n * factorial(n-1); } int combination(int m, int n) { return factorial(m)/(factorial(n)*factorial(m-n)); } int main() { printf("C(5,2)=%d", combination(5,2)); // 输出10 return 0; }
四、函数的递归调用
1. 递归三要素
-
递归出口:终止条件
-
递归体:问题分解
-
收敛性:问题规模逐渐减小
2. 示例:斐波那契数列
int fibonacci(int n) { if(n == 0) return 0; // 递归出口 if(n == 1) return 1; // 递归出口 return fibonacci(n-1) + fibonacci(n-2); // 递归体 }
3. 递归与迭代对比
特性 | 递归 | 迭代 |
---|---|---|
代码简洁性 | 高 | 低 |
内存消耗 | 大(栈空间) | 小 |
性能 | 较低(函数调用开销) | 较高 |
适用问题 | 树形结构、分治 | 线性过程 |
五、数组作为函数参数
1. 传递方式
-
数组名作为指针传递:实际传递的是数组首地址
-
大小信息丢失:需要额外传递数组长度
2. 三种形式
// 形式1:指针形式 void func1(int *arr, int size); // 形式2:数组形式(编译器仍视为指针) void func2(int arr[], int size); // 形式3:指定大小(不灵活) void func3(int arr[10]);
3. 示例:数组求和
int arraySum(int arr[], int size) { int sum = 0; for(int i=0; i<size; i++) { sum += arr[i]; } return sum; } int main() { int nums[] = {1,2,3,4,5}; printf("Sum: %d", arraySum(nums, 5)); return 0; }
4. 多维数组传递
// 必须指定第二维及以后的大小 void printMatrix(int mat[][3], int rows) { for(int i=0; i<rows; i++) { for(int j=0; j<3; j++) { printf("%d ", mat[i][j]); } printf("\n"); } } int main() { int matrix[2][3] = {{1,2,3},{4,5,6}}; printMatrix(matrix, 2); return 0; }
六、综合应用示例
学生成绩统计系统
#include <stdio.h> // 函数声明 void inputScores(float scores[], int n); float calculateAverage(float arr[], int size); void printAboveAverage(float scores[], int n, float avg); int main() { const int NUM = 5; float scores[NUM], avg; // 嵌套调用示例 inputScores(scores, NUM); avg = calculateAverage(scores, NUM); printAboveAverage(scores, NUM, avg); return 0; } void inputScores(float scores[], int n) { printf("请输入%d个学生成绩:\n", n); for(int i=0; i<n; i++) { scanf("%f", &scores[i]); } } float calculateAverage(float arr[], int size) { float sum = 0; for(int i=0; i<size; i++) { sum += arr[i]; } return sum/size; } void printAboveAverage(float scores[], int n, float avg) { printf("高于平均分(%.1f)的学生:\n", avg); for(int i=0; i<n; i++) { if(scores[i] > avg) { printf("学生%d: %.1f\n", i+1, scores[i]); } } }
七、关键注意事项
-
数组参数传递:
-
实际传递的是指针,不是数组副本
-
函数内对数组元素的修改会影响原数组
-
-
递归效率:
-
避免重复计算(如斐波那契数列的朴素递归效率极低)
-
可考虑使用"记忆化"技术优化
-
-
函数原型:
-
现代C编程建议总是提供函数原型
-
将函数声明放在头文件中
-
-
参数检查:
-
对指针参数检查NULL
-
对数组参数检查有效范围
-
-
可重入性:
-
避免在函数中使用全局变量
-
确保函数线程安全
-