系好安全带,这需要几分钟。
当您使用参数(参数)调用函数时,对于函数如何处理这些参数,有几种不同的评估策略。C 使用的一种称为“按值调用”。当你调用一个函数时
foo( x, y, z );
每个表达式 x
、y
和z
都被完全评估,这些评估的结果就是传递给函数的结果。在函数定义中
void foo( int a, int b, int c )
{
a = b + c;
}
每个形式参数a
、b
和c
是内存中的不同对象,x
它们接收 、 和 的值y
,但更新不会以任何方式影响。这是一个直接说明这一点的示例:z
x
y
z
a
x
#include <stdio.h>
void foo( int a, int b, int c )
{
a = b + c;
printf( "a = %d\n", a );
}
int main( void )
{
int x = 1;
int y = 2;
printf( "before foo: x = %d, y = %d\n" );
foo( x, y, x + y );
printf( "after foo: x = %d, y = %d\n" );
return 0;
}
运行时,此代码给出输出:
before foo: x = 1, y = 2
a = 5
after foo: x = 1, y = 2
如您所见,更新a
对 的值没有影响x
。
为了让函数改变参数的值,我们必须传递一个指向该参数的指针。如果我们希望更改 ina
反映在 中x
,我们必须使用一元(address-of) 运算符在函数调用中传递地址,并使用一元 ( dereference ) 运算符在函数定义中取消引用它:x
&
*
#include <stdio.h>
void foo( int *a, int b, int c )
{
*a = b + c;
printf( "*a = %d\n", *a );
}
int main( void )
{
int x = 1;
int y = 2;
printf( "before foo: x = %d, y = %d\n", x, y );
foo( &x, y, x + y );
printf( "after foo: x = %d, y = %d\n", x, y );
return 0;
}
在这种情况下,a
不会收到 的值x
,而是收到它的地址,给我们这种情况:
a == &x // int * == int *
*a == x // int == int
表达式 *a
infoo
充当 in 的同义词x
,因此main
向 写入新值*a
等同于向 写入新值x
。
最终,这就是为什么您需要在调用时将指针传递给要更新的内容scanf
(或任何其他需要更新参数的函数)。
注意- 数组很奇怪。除非它是sizeof
或一元运算符的操作数&
,或者是用于在声明中初始化字符数组的字符串字面量,否则“N 元素数组”类型的表达式T
将被转换或“衰减”为键入“pointer to T
”,表达式的值将是数组第一个元素的地址。换句话说,如果你有这样的代码:
int arr[10];
...
bar( arr );
编译器将arr
函数调用中的表达式替换为等效于 的表达式&arr[0]
,因此bar
它不会获取整个数组的副本,而是获取第一个元素的地址:
void bar( int *a )
{
...
}
这就是为什么在读取字符串时不使用运算符的原因- 您将数组表达式作为参数传递,它在调用之前隐式转换为指针:&
scanf
int val;
char name[10];
scanf( "%d %s", &val, name );
这种行为是有原因的,但这有点超出了本次讨论的范围。请记住,数组很奇怪。