在C语言开发中,函数参数传递是核心基础知识点,指针和数组作为两种常用的参数类型,其传递逻辑和值传递、地址传递的关联常常让开发者产生困惑。理解两者的差异,能帮助我们更精准地控制函数对数据的操作。

值传递与地址传递的核心概念
值传递的本质是函数调用时,将实参的值复制一份传递给形参,形参和实参是两个独立的内存单元,函数内对形参的修改不会影响实参的原始值。而地址传递则是将实参的内存地址传入函数,形参指向实参所在的内存空间,函数内通过地址修改数据时,会直接改变原实参的内容。
值传递示例
下面是一个典型的值传递例子,传入普通整型变量:
#include <stdio.h>
// 值传递函数,修改形参x的值
void value_pass(int x) {
x = 20;
printf("函数内x的值:%dn", x);
}
int main() {
int a = 10;
value_pass(a);
printf("函数外a的值:%dn", a);
return 0;
}
程序运行后,函数内x的值为20,但函数外a的值仍然是10,说明值传递不会修改原实参。
地址传递示例
下面是通过指针实现地址传递的例子:
#include <stdio.h>
// 地址传递函数,通过指针修改实参的值
void addr_pass(int *p) {
*p = 20;
printf("函数内*p的值:%dn", *p);
}
int main() {
int a = 10;
addr_pass(&a);
printf("函数外a的值:%dn", a);
return 0;
}
运行后可以看到,函数内修改指针指向的内容后,原变量a的值也变成了20,这就是地址传递的效果。
指针作为函数参数的传递特点
当指针作为函数参数时,传递的是指针变量本身的值,也就是所指向的内存地址,这属于值传递的一种特殊形式:传递的是地址值。如果函数内修改指针形参的指向,不会影响原指针实参的指向;但如果通过指针形参修改其指向地址的内容,就会影响原数据。
#include <stdio.h>
// 修改指针形参的指向
void change_ptr(int *p) {
int b = 30;
p = &b; // 修改形参p的指向,不影响原实参
printf("函数内p指向的值:%dn", *p);
}
int main() {
int a = 10;
int *ptr = &a;
printf("修改前ptr指向的值:%dn", *ptr);
change_ptr(ptr);
printf("修改后ptr指向的值:%dn", *ptr);
return 0;
}
运行结果显示,函数内修改了形参p的指向,但原指针ptr依然指向a,值还是10,说明指针作为参数时,指针本身的传递是值传递。
数组作为函数参数的传递特点
数组作为函数参数时,无论形参是写成数组形式还是指针形式,实际传递的都是数组首元素的地址,属于地址传递。因为数组名在大多数语境下会隐式转换为指向首元素的指针,所以数组参数的本质是指针参数。
#include <stdio.h>
// 两种等价的数组参数写法
void print_arr1(int arr[], int len) {
for (int i = 0; i < len; i++) {
printf("%d ", arr[i]);
}
printf("n");
}
void print_arr2(int *arr, int len) {
for (int i = 0; i < len; i++) {
printf("%d ", arr[i]);
}
printf("n");
}
int main() {
int arr[5] = {1, 2, 3, 4, 5};
print_arr1(arr, 5);
print_arr2(arr, 5);
return 0;
}
上面的两个函数写法完全等价,形参int arr[]和int *arr没有区别,都是接收数组首元素的地址。
指针与数组参数传递的核心差异
两者的核心差异体现在参数本质和内存操作上:
- 指针作为参数是传递指针变量存储的地址值,属于地址值的值传递,既可以修改指向的内容,也可以尝试修改指针本身的指向(不影响原指针)。
- 数组作为参数时,传递的是数组首元素的地址,形参会被当作指针处理,无法直接获取原数组的长度,需要额外传入长度参数。
- 如果在函数内修改数组参数的元素,会直接修改原数组的内容,这和地址传递的特性一致。
数组参数修改原数组示例
#include <stdio.h>
// 修改数组元素的值
void modify_arr(int arr[], int len) {
for (int i = 0; i < len; i++) {
arr[i] = arr[i] * 2;
}
}
int main() {
int arr[3] = {1, 2, 3};
printf("修改前数组:");
for (int i = 0; i < 3; i++) {
printf("%d ", arr[i]);
}
printf("n");
modify_arr(arr, 3);
printf("修改后数组:");
for (int i = 0; i < 3; i++) {
printf("%d ", arr[i]);
}
printf("n");
return 0;
}
运行后可以看到原数组的元素都被翻倍,说明数组参数传递后修改会影响原数组。
总结对比
为了更清晰地展示差异,我们可以通过表格对比:
| 对比项 | 指针作为参数 | 数组作为参数 |
|---|---|---|
| 传递本质 | 传递指针存储的地址值(值传递) | 传递数组首元素地址(地址传递) |
| 能否修改原数据 | 可以通过解引用修改指向的内容 | 可以直接修改数组元素 |
| 形参本质 | 指针变量 | 会被当作指针处理 |
| 长度信息 | 需要额外传递 | 需要额外传递 |
在实际开发中,需要根据需求选择参数类型:如果只需要操作单个数据,用指针参数更灵活;如果需要操作一组连续的数据,用数组形式的参数可读性更好,但本质上还是指针操作。