系好安全带,这需要几分钟。
当您使用参数(参数)调用函数时,对于函数如何处理这些参数,有几种不同的评估策略。C 使用的一种称为“按值调用”。当你调用一个函数时
foo( x, y, z );
每个表达式 x、y和z都被完全评估,这些评估的结果就是传递给函数的结果。在函数定义中
void foo( int a, int b, int c )
{
a = b + c;
}
每个形式参数a、b和c是内存中的不同对象,x它们接收 、 和 的值y,但更新不会以任何方式影响。这是一个直接说明这一点的示例:zxyzax
#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
表达式 *ainfoo充当 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 );
这种行为是有原因的,但这有点超出了本次讨论的范围。请记住,数组很奇怪。