Pointers on C——7 Functions.3

7.3 Function Arguments


All arguments to C functions are passed with a technique known as call by value,which means that the function gets a copy of the argument value. Thus the function may modify its parameters without fear of affecting the values of the arguments passed from the calling program. This behavior is the same as value (not var)parameters in Modula and Pascal.

C 函数的所有参数均以"传值调用"方式进行传递,这意味着函数将获得参数值的一份拷贝。这样,函数可以放心修改这个拷贝值,而不必担心会修改调用程序实际传递给它的参数。这个行为与Modula 和Pascal 中的值参数(不是var参数)相同。


The rule in C is simple: all arguments are passed by value. However, if an array name is passed as an argument and a subscript is used on the argument in the function, then modifying array elements in the function actually changes the elements of the array in the calling program. The function accesses the very same array that exists in the calling program; the array is not copied. This behavior is termed call by reference and is how var parameters are implemented in many other languages.

C 的规则很简单:所有参数都是传值调用。但是,如果被传递的参数是一个数组名,并且在函数中使用下标引用该数组的参数,那么在函数中对数组元素进行修改实际上修改的是调用程序中的数组元素。函数将访问调用程序的数组元素,数组并不会被复制。这个行为被称为"传址调用",也就是许多其他语言所实现的var 参数。


The behavior with array arguments seems to contradict the call by value rule.However there isnʹt a contradiction—the value of the array name is really a pointer,and a copy of the pointer is passed to the function. A subscript is really a form of indirection and applying indirection to the pointer accesses the locations that it points to. The argument (the pointer) is indeed a copy, but the indirection uses the copy to access the original array values. We return to this point in the next chapter, but for now remember these two rules:

数组参数的这种行为似乎与传值调用规则相悖。但是,此处其实并无矛盾之处——数组名的值实际上是一个指针,传递给函数的就是这个指针的一份拷贝。下标引用实际上是间接访问的另一种形式,它可以对指针执行间接访问操作,访问指针指向的内存位置。参数(指针)实际上是一份拷贝,但在这份拷贝上执行间接访问操作所访问的是原先的数组。我们将在下一章再讨论这一点,此处只要记住两个规则:


1. Scalar arguments to a function are passed by value.

传递给函数的标量参数是传值调用的。

2. Array arguments to a function behave as though they are passed by reference.

传递给函数的数组参数在行为上就像它们是通过传址调用的那样。


/*

** Check the value for even parity.

*/

int

even_parity( int value, int n_bits )

{

int parity = 0;

/*

** Count the number of 1-bits in the value.

*/

while( n_bits > 0 ){

parity += value & 1;

value >>= 1;

n_bits -= 1;

}

/*

** Return TRUE if the low order bit of the count is zero

** (which means that there were an even number of 1's).

*/

return ( parity % 2 ) == 0;

}

Program 7.2 Parity check


Program 7.2 illustrates the call‐by‐value behavior of scalar function arguments. This function checks whether the first argument has even parity, that is, if the number of 1ʹs it contains is an even number. The second argument specifies the number of significant bits in the value. The function works by shifting the value by one bit, over and over, so that every bit appears sooner or later in the right‐most position. The bits are added together, one by one, so that after the loop ends we have a count of the number of 1ʹs in the original value. Finally, the count is tested to see if its least significant bit is set. If not, the number of 1ʹs was even.

程序7.2 说明了标量函数参数的传值调用行为。函数检查第1 个参数是否满足偶校验,也就是它的二进制位模式中1 的个数是否为偶数。函数的第2 个参数指定第1 个参数中有效位的数目。函数一次一位地对第1 个参数值进行移位,所以每个位迟早都会出现在最右边的那个位置。所有的位逐个加在-起,所以在循环结束之后,我们就得到第1 个参数值的位模式中1 的个数。最后,对这个数进行测试,看看它的最低有效位是不是1 。如果不是,那么说明l 的个数就是偶数个。


The interesting feature of this function is that it destroys both of its arguments as the work progresses. This technique works fine, because with call‐by‐value the argumentsʹ are copies of the callerʹs values. Destroying the copies does not affect the original values.

这个函数的有趣特性是在它的执行过程中,它会破坏这两个参数的值。但这并无妨,因为参数是通过传值调用的,函数所使用的值是实际参数的一份拷贝。破坏这份拷贝并不会影响原先的值。

/*

** Exchange two integers in the calling program (doesn't work!).

*/

void

swap( int x, int y )

{

int temp;

temp = x;

x = y;

y = temp;

}

Program 7.3a Swap integers: bad version


Program 7.3a is different; it wants to modify the callerʹs arguments. The intent of this function is to exchange the contents of two arguments in the calling program. It doesnʹt work, though, because all that is exchanged are the copies of the values that were sent to the function. The original values are untouched.

程序7.3a 则有所不同:它希望修改调用程序传递的参数。这个函数的目的是交换调用程序所传递的这两个参数的值。但这个程序是无效的,因为它实际交换的是参数的拷贝,原先的参数值并未进行交换。


To access the callerʹs values you must pass pointers to the locations you wish to modify. The function must then use indirection to follow the pointers and modify the desired locations. Program 7.3b uses this technique. Because the function expects pointers as arguments, we would call it like this:

为了访问调用程序的值,你必须向函数传递指向你希望修改的变量的指针。接着函数必须对指针使用间接访问操作,修改需要修改的变量。程序7.3b 使用了这个技巧:


/*

** Exchange two integers in the calling program.

*/

void

swap( int *x, int *y )

{

int temp;

temp = *x;

*x = *y;

*y = temp;

}

Program 7.3b Swap integers: good version

swap( &a, &b );

/*

** Set all of the elements of an array to zero.

*/

void

clear_array( int array[], int n_elements )

{

/*

** Clear the elements of the array starting with the last

** and working towards the first. Note the predecrement

** avoids going off the end of the array.

*/

while( n_elements > 0 )

array[ --n_elements ] = 0;

}

Program 7.4 Set an array to zero


Program 7.4 sets all of the elements of an array to zero, n_elements is a scalar so it is passed by value; modifying its value in the function does not affect the corresponding argument in the calling program. On the other hand, the function does indeed set the elements of the calling programʹs array to zero. The value of the array argument is a pointer, and the subscript performs indirection with this pointer.

程序7 .4把一个数组的所有元素都设置为0。n-elements 是一个标量参数,所以它是传值调用的。在函数中修改它的值并不会影响调用程序中的对应参数。另一方面,函数确实把调用程序的数组的所有元素设置为0 。数组参数的值是一个指针,下标引用实际上是对这个指针执行间接访问操作。


This example also illustrates another feature. It is legal to declare array parameters without specifying a size because memory is not allocated for the array elements in the function; the indirection causes the array elements in the calling program to be accessed instead. Thus, a single function can access array of any size,which should excite Pascal programmers. However, there isnʹt any way for the function to figure out the actual size of an array argument, so this information must also be passed explicitly if it is needed.

这个例子同时说明了另外一个特性。在声明数组参数时不指定它的长度是合法的,因为函数并不为数组元素分配内存。间接访问操作将访问调用程序中的数组元素。这样,一个单独的函数可以访问任意长度的数组。对于Pascal 程序员而言,这应该是个福音。但是,函数并没有办法判断数组参数的长度,所以函数如果需要这个值,它必须作为参数显式地传递给函数


Recall that  in K&R C, function parameters were declared like this:

int

func( a, b, c )

int a;

char b;

float c;

{

...

Another reason to avoid this style is that K&R compilers handled arguments a little differently: char and short arguments were promoted to int before being passed, and float arguments were promoted to double‐ These conversions are called the default argument promotions, and because of them you will frequently see function parameters in pre‐ANSI programs declared as int when in fact char values are passed.

避免使用这种旧风格的另一个理由是K&R 编译器处理参数的方式稍有不同:在参数传递之前,char 和short 类型的参数被提升为int 类型. float 类型的参数被提升为double 类型。这种转换被称为缺省参数提升(default argument promotion) 。由于这个规则的存在,在ANSI 标准之前的程序中,你会经常看到函数参数被声明为int 类型,但实际上传递的是char 类型。


To maintain compatibility, ANSI compilers also perform these conversions for functions declared in the old style. They are not done on functions that have been prototyped, though, so mixing the two styles can lead to errors.

为了保持兼容性, ANSI 编译器也会为旧式风格声明的函数执行这类转换。但是,使用原型的函数并不执行这类转换,所以混用这两种风格可能导致错误。


上一章 Pointers on C——7 Functions.2


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值