C语言函数调用、声明、嵌套与递归详解

一、函数的调用

1. 函数调用的基本形式

返回值变量 = 函数名(实参列表);  // 对有返回值函数
函数名(实参列表);           // 对无返回值函数

2. 调用方式

  • 单独调用printf("Hello");

  • 表达式调用int sum = add(3,5) * 2;

  • 参数调用printf("%d", add(2,3));

3. 调用过程

  1. 为形参分配内存

  2. 将实参值传递给形参(值传递)

  3. 执行函数体

  4. 返回返回值(如果有)

  5. 释放形参内存

二、函数的声明

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]);
        }
    }
}

七、关键注意事项

  1. 数组参数传递

    • 实际传递的是指针,不是数组副本

    • 函数内对数组元素的修改会影响原数组

  2. 递归效率

    • 避免重复计算(如斐波那契数列的朴素递归效率极低)

    • 可考虑使用"记忆化"技术优化

  3. 函数原型

    • 现代C编程建议总是提供函数原型

    • 将函数声明放在头文件中

  4. 参数检查

    • 对指针参数检查NULL

    • 对数组参数检查有效范围

  5. 可重入性

    • 避免在函数中使用全局变量

    • 确保函数线程安全

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值