跳转到内容

C 语言函数参数

来自代码酷


C语言函数参数是函数与外部代码交互的重要媒介,通过参数传递数据实现模块化编程。本文将系统讲解参数传递机制、形式参数与实际参数的区别、传值与传址的区别,以及可变参数等高级用法。

基本概念[编辑 | 编辑源代码]

函数参数分为两类:

  • 形式参数(形参):函数定义时声明的变量,用于接收外部传入的值
  • 实际参数(实参):函数调用时传递给函数的实际值

示例:

// 形参:a和b
int add(int a, int b) {
    return a + b;
}

int main() {
    int x = 5, y = 3;
    // 实参:x和y
    int sum = add(x, y);
    return 0;
}

参数传递方式[编辑 | 编辑源代码]

传值调用(Call by Value)[编辑 | 编辑源代码]

默认的参数传递方式,将实参的值复制给形参。函数内对形参的修改不会影响原始变量。

void modify(int num) {
    num = 100; // 只修改局部副本
    printf("函数内: %d\n", num);
}

int main() {
    int n = 50;
    modify(n);
    printf("主函数: %d\n", n); // 原值不变
    return 0;
}

输出:

函数内: 100
主函数: 50

传址调用(Call by Reference)[编辑 | 编辑源代码]

通过指针传递变量地址,允许函数修改原始变量。

void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

int main() {
    int x = 10, y = 20;
    swap(&x, &y);
    printf("x=%d, y=%d", x, y);
    return 0;
}

输出:

x=20, y=10

参数传递对比[编辑 | 编辑源代码]

flowchart LR A[实参类型] -->|基本类型| B[传值调用] A -->|数组/指针| C[传址调用] B --> D[创建副本] C --> E[操作原数据]

数组作为参数[编辑 | 编辑源代码]

数组名作为参数时自动退化为指针,实际传递的是数组首地址:

void printArray(int arr[], int size) {
    for(int i=0; i<size; i++) {
        printf("%d ", arr[i]);
    }
}

int main() {
    int nums[5] = {1,2,3,4,5};
    printArray(nums, 5); // 等价于 printArray(&nums[0], 5)
    return 0;
}

高级参数特性[编辑 | 编辑源代码]

默认参数(C99扩展)[编辑 | 编辑源代码]

GCC等编译器支持通过宏模拟默认参数:

#define DEFAULT_FUNC(a, ...) func(a, ##__VA_ARGS__)

void func(int x, int y) {
    y = y ? y : 10; // 默认值10
    printf("%d, %d", x, y);
}

int main() {
    DEFAULT_FUNC(5);    // 输出 5, 10
    DEFAULT_FUNC(5,20); // 输出 5, 20
    return 0;
}

可变参数[编辑 | 编辑源代码]

使用<stdarg.h>处理不定数量参数:

#include <stdarg.h>

double average(int count, ...) {
    va_list ap;
    double sum = 0;
    va_start(ap, count);
    for(int i=0; i<count; i++) {
        sum += va_arg(ap, double);
    }
    va_end(ap);
    return sum/count;
}

int main() {
    printf("%.2f", average(3, 1.5, 2.5, 3.5));
    return 0;
}

输出:

2.50

参数传递的数学表示[编辑 | 编辑源代码]

对于函数f(x)

  • 传值调用:f(x)操作的是x的副本
  • 传址调用:解析失败 (语法错误): {\displaystyle f(&x)} 操作的是x的内存地址

实际应用案例[编辑 | 编辑源代码]

场景:实现动态数组处理函数

void processArray(int *arr, int size, int (*processor)(int)) {
    for(int i=0; i<size; i++) {
        arr[i] = processor(arr[i]);
    }
}

int square(int x) { return x*x; }

int main() {
    int data[] = {1,2,3,4,5};
    processArray(data, 5, square);
    // 输出: 1 4 9 16 25
    return 0;
}

最佳实践[编辑 | 编辑源代码]

1. 明确参数传递方式:修改原数据使用指针,否则用传值 2. 数组参数总是伴随长度参数 3. 复杂结构体优先使用指针传递 4. 避免过多参数(通常不超过5个) 5. 使用const保护不应修改的参数

常见问题[编辑 | 编辑源代码]

模板:Collapse

模板:Collapse

通过系统学习函数参数机制,开发者可以更灵活地设计函数接口,构建可维护的模块化代码结构。