C函数的参数传递

C函数的参数分为实参和形参。

总的来说,实参和形参是分开的,形参的值无论如何都不会改变实参值。但可以通过指针改变指针指向的内存地址的值。

C语言采用的是值传递,无论形参如何变化,都不会改变实参的值。如果参数是指针,改变的是指针所指的地址中的内容。但参数的指针无论如何变化,都不会改变。

首先来看普通变量的参数传递。

void func(int arg) {
	arg += 1;
}

void main(){
	int a = 0;
	func(a); // a的值为0
}

变量a初始化为0,执行func函数后依旧为0。因此在普通变量下实参和形参不影响。

那如果数组变量呢?

void func(int arg[]) {
	arg[0] += 1;
	arg[1] += 1;
}

void main(){
	int a[] = { 0, 1};
	func(a); // 数组a的值变成了{1, 2}
}

为什么数组的值改变了呢?因为数组本身是指针变量。数组名表示的是数组的首地址,而数组后面的下标表示数组地址上的值。我们调用函数时传递的是数组的首地址,而函数改变的是这个地址上后续的值。

void func(int arg[]) {
	arg += 1;
}

void main(){
	int a[] = { 0, 1};
	func(a); // 数组a的首地址保持不变
}

上面的代码执行后a的首地址保持不变。其中有趣的地方在于形参。

我们知道,变量的地址是无法通过赋值改变的,但指针的地址可以。下面的代码编译器是无法通过的,因为赋值语句在尝试修改数组的首地址,这是不允许的。

void main(){
	int a[] = { 0 };
	a += 1; // 编译器无法通过
}

但上面的代码中,函数func拥有形参数组a,而在函数体中,修改了数组的首地址,这语句编译器通过了。说明形参“int a[]”的本质不是数组,而是指向数组首地址的指针。

因为指针变量允许修改变量中的地址值。在不用const修饰的情况下,也允许修饰地址指向的内存的值。

对于数组来说,可以把形参都看成临时的指针变量,无论如何修改指针的变量,改变的只是当前指针的值。而不会改变指针指向地址的值。也就是不会改变数组的值。

只有修改指针变量指向内存地址的值,实参才会改变。


既然数组的本质是地址,那形参作为指针变量也很容易理解了。

void func(int* arg) {
	arg += 1;
}

void main(){
	int a = 0;
	func(&a); // a的值依旧是0
}

但如果使用的是星号,那变量a的值会发生改变。

void func(int* arg) {
	*arg += 1;
}

void main(){
	int a = 0;
	func(&a); // a的值变为了1
}