- 参数的定义:什么是参数?
- 形参与实参:两者之间的区别与联系。
- 参数传递方式:C 语言如何传递参数?
- 不同类型的参数:值传递、指针传递、数组名传递。
- 可变参数:如何处理参数数量不固定的函数?
- 最佳实践与注意事项。
参数的定义
在 C 语言中,参数是函数定义和函数调用之间的信息桥梁,它们允许你向函数传递数据,使函数能够根据这些数据执行不同的操作。

- 函数定义:在函数定义时,参数被称为形式参数(Formal Parameters),简称形参,它们是函数括号内声明的变量,用于接收传递进来的值。
- 函数调用:在调用函数时,传递给函数的实际值被称为实际参数(Actual Arguments),简称实参,实参可以是常量、变量、表达式或函数调用。
简单比喻: 想象一下你点外卖:
- 函数:就是那个“做菜的厨房”。
- 函数定义:厨房的菜单上写着“需要一份肉和一份菜才能做这道菜”,这里的“肉”和“菜”就是形参,它们是厨房接收食材的“容器”或“要求”。
- 函数调用:你打电话给厨房说“我要一份牛肉和一份西兰花”,这里的“牛肉”和“西兰花”就是实参,是你实际传递给厨房的食材。
形参与实参
| 特性 | 形参 | 实参 |
|---|---|---|
| 位置 | 在函数定义的函数头中声明 | 在函数调用的函数名后括号内 |
| 作用 | 接收实参传递过来的值 | 将值传递给形参 |
| 本质 | 是一个变量,有类型和名称 | 是一个值,可以是常量、变量、表达式 |
| 内存 | 在函数被调用时创建,函数结束时销毁 | 在函数调用时,将其值复制给形参 |
示例代码:
#include <stdio.h>
// 函数定义
// a 和 b 是形参
int add(int a, int b) {
printf("Inside function: a = %d, b = %d\n", a, b);
return a + b;
}
int main() {
int x = 5;
int y = 10;
// 函数调用
// x 和 y 是实参
int sum = add(x, y);
printf("In main: sum = %d\n", sum);
return 0;
}
输出:
Inside function: a = 5, b = 10
In main: sum = 15
在这个例子中,main 函数中的变量 x 和 y(实参)的值被复制给了 add 函数中的变量 a 和 b(形参)。

参数传递方式:值传递
C 语言中,所有参数传递都是通过“值传递”(Pass-by-Value)实现的。
这意味着:
- 当函数被调用时,会为形参创建新的、独立的内存空间。
- 将实参的值复制到这些新的内存空间中。
- 函数内部对形参的任何修改,都只影响它自己的那份副本,不会影响原始实参的值。
示例:证明值传递
#include <stdio.h>
void modifyValue(int num) { // num 是形参
printf("Inside modifyValue (before change): num = %d\n", num);
num = 100; // 修改的是 num 这个副本
printf("Inside modifyValue (after change): num = %d\n", num);
}
int main() {
int myNum = 10;
printf("In main (before calling function): myNum = %d\n", myNum);
modifyValue(myNum); // myNum 是实参
printf("In main (after calling function): myNum = %d\n", myNum);
return 0;
}
输出:

In main (before calling function): myNum = 10
Inside modifyValue (before change): num = 10
Inside modifyValue (after change): num = 100
In main (after calling function): myNum = 10
可以看到,modifyValue 函数内部将 num 修改为 100,但 main 函数中的 myNum 仍然是 10,这证明了传递的是一份副本。
不同类型的参数(高级应用)
既然是值传递,那如何才能在函数内部修改外部变量的值呢?答案是传递变量的地址,这就是指针的用武之地。
1 传递指针(模拟引用传递)
通过传递变量的地址,我们可以在函数内部通过指针间接访问和修改原始变量。
示例:通过指针修改外部变量
#include <stdio.h>
void modifyValueByPointer(int *ptr) { // ptr 是一个指向整形的指针(形参)
printf("Inside function (before change): *ptr = %d\n", *ptr);
*ptr = 100; // 通过解引用指针来修改 ptr 所指向的内存中的值
printf("Inside function (after change): *ptr = %d\n", *ptr);
}
int main() {
int myNum = 10;
printf("In main (before calling function): myNum = %d\n", myNum);
// 注意这里传递的是变量的地址 &myNum
modifyValueByPointer(&myNum);
printf("In main (after calling function): myNum = %d\n", myNum);
return 0;
}
输出:
In main (before calling function): myNum = 10
Inside function (before change): *ptr = 10
Inside function (after change): *ptr = 100
In main (after calling function): myNum = 100
这次,main 函数中的 myNum 被成功修改了,因为我们传递的是 myNum 的地址,函数内部的 *ptr 操作直接作用于 myNum 所在的内存。
2 传递数组名
数组名作为函数参数时,有一个特殊的性质:数组名会“退化”为其首元素的地址。
传递数组名本质上是传递一个指针。
示例:传递数组
#include <stdio.h>
// 接收一个整型数组,arr 是一个指向 int 的指针
void printArray(int arr[], int size) {
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
int main() {
int numbers[] = {1, 2, 3, 4, 5};
int size = sizeof(numbers) / sizeof(numbers[0]);
// 传递数组名 numbers
printArray(numbers, size);
return 0;
}
在 printArray 函数中,int arr[] 和 int *arr 在功能上是等价的,函数内部对 arr[i] 的操作,实际上是通过指针算术运算访问原始数组 numbers 的元素,因此可以修改数组内容。
可变参数
C 语言提供了一种机制,允许函数接受数量可变的参数,最经典的例子就是 printf 函数。
要创建可变参数函数,需要包含 <stdarg.h> 头文件,并使用以下宏:
va_list: 一个类型,用于存储可变参数列表。va_start(ap, last_fixed_arg): 初始化va_list变量ap,last_fixed_arg是最后一个固定的参数。va_arg(ap, type): 从ap中获取下一个参数,type是该参数的类型。va_end(ap): 清理va_list。
示例:计算可变参数的平均值
#include <stdio.h>
#include <stdarg.h> // 必须包含此头文件
// last_fixed_arg 是 count
double average(int count, ...) {
va_list args; // 声明一个 va_list 变量
double sum = 0.0;
// 初始化 args,count 是最后一个固定参数
va_start(args, count);
// 循环 count 次,从 args 中获取每个参数
for (int i = 0; i < count; i++) {
// va_arg 的第二个参数是你要获取的参数的类型
sum += va_arg(args, int);
}
// 清理 args
va_end(args);
return sum / count;
}
int main() {
printf("Average of 2, 3, 4, 5 is: %.2f\n", average(4, 2, 3, 4, 5));
printf("Average of 10, 20 is: %.2f\n", average(2, 10, 20));
return 0;
}
输出:
Average of 2, 3, 4, 5 is: 3.50
Average of 10, 20 is: 15.00
注意: 使用可变参数函数时,你必须非常清楚参数的类型和数量,否则会导致未定义行为(比如程序崩溃)。
最佳实践与注意事项
-
使用
const保护数据:如果函数只需要读取参数的值,而不需要修改它,最好在形参前加上const关键字,这可以防止函数内部意外修改数据,同时也能让代码意图更清晰。// 好的实践 int calculateLength(const char *str); // 函数承诺不会修改 str 指向的内容
-
指针参数要明确:如果参数是指针,一定要在注释或函数名中明确说明该指针是用于输入、输出,还是两者兼具。
- 输入指针:
void processData(const Data* data); - 输出指针:
void getResult(int* result);
- 输入指针:
-
传递大型结构体时使用指针:如果函数需要接收一个很大的结构体作为参数,直接传递结构体(值传递)会导致整个结构体被复制,造成性能开销,这时应该传递指向该结构体的指针。
// 低效 void processBigStruct(BigStruct bs); // 高效 void processBigStruct(const BigStruct* bs_ptr);
-
注意数组长度:当传递数组时,最好同时传递数组的长度,以防止数组越界访问。
| 概念 | 关键点 |
|---|---|
| 参数 | 函数间传递数据的桥梁。 |
| 形参 vs 实参 | 形参是函数定义中的变量,实参是调用时传递的具体值。 |
| 传递方式 | C 语言只有值传递。 |
| 如何修改外部变量 | 传递变量的地址(即使用指针)。 |
| 数组作为参数 | 数组名会退化为指针,传递的是首元素地址。 |
| 可变参数 | 使用 <stdarg.h> 中的宏实现,需谨慎使用。 |
| 最佳实践 | 对只读数据使用 const,大型结构体传指针,明确指针用途。 |
