4.5 将数组传递给函数
要将数组参数传递给函数,需指定不带方括号的数组名。例如,如果数组hourlyTemperatures声明如下:
int hourlyTemperatures[24];
则下列函数调用语句:
modifyArray(hourlyTemperatutes,24);
将数组 hourlyTemperatures 及其长度传递给函数 modifyArray。将数组传递给函数时,通常也将其长度传递给函数,使函数能处理数组中特定的元素个数(否则要在被调用函数中建立这些信息,甚至要把数组长度放在全局变量中)。第8章介绍Array类时,将把数组长度设计在用户自定义类型中,每个Array对象生成时都“知道”自己的长度。这样,将Array对象传递给函数时,就不用把数组长度作为参数一起传递。
C++使用模拟的按引用调用,自动将数组传递绐函数,被调用函数可以修改调用者原数组中的元素值。数组名的值为数组中第一个元素的地址。由于传递数组的开始地址,因此被调用函数知道数组的准确存放位置。因此,被调用函数在函数体中修改数组元素时.实际上是修改原内存地址中的数组元素。
性能提示 4.4
模拟按引用调用传递数组时才能有性能上的意义。如果数组按值传递,则是传递每个元素的副本。对于经常传递的大数组,这是很费时间的,而且存放数组副本要占用很大空间。
软件工程视点 4.2
也可以按值传递数组(用第6章介绍的简单方法),但很少使用。
尽管模拟按引用调用传递整个数组,但各个数组元素和简单变量一样是按值传递。这种简单的单个数据称为标量(scalar 或scalar quanity)。要将数组元素传递给函数,用数组元素的下标名作为函数调用中的参数。第5章将介绍标量(即各个变量和数组元素)的模拟按引用调用。
要让函数通过函数调用接收数组,函数的参数表应指定接收数组。例如,函数modifyArray的函数苜部可能如下所示:
void modifyArray(int b[],int arraySize)
表示modifyArray要在参数b中接收整型数组并在参数arraySize中接收数组元素个数。数组方括号中的数组长度不是必需的,如果包括,则编译器将其忽略。由于模拟按引用调用传递数组,因此被调用函数使用数组名b时,实际上引用调用者的实际数组(上例中为数组hourlyTemperatures)。第5章介绍表示函数接收数组的其他符号,这些符号基于数组与指针之间的密切关系。
注意modlfyArray函数原型的表示方法:
void modifyArray(int[],int);
这个原型也可以改写成:
void modifyArray( int anyArrayName[],int( anyVariableName )
但第3章曾介绍过,C++编译器忽略函数原型中的变量名。
编程技巧 4.3
有些程序员在函数原型中包括变量名,使程序更清晰,编译器将忽略这个名称。
记住,函数原型告诉编译器参数个数和参数类型(按参数出现的顺序)。
图 4.14 的程序演示了传递整个数组与传递数组元素之间的差别。程序首先打印整型数组a的五个元素,然后将a及其长度传递给函数 modifyArray,其中将a数组中的元素乘以2,然后在main中重新打印 a。从输出可以看出,实际由modifyAnay修改a的元素。现在程序打印a[3]的值并将其传递给函数 modifyElement。函数 modifyElement 将参数乘以 2。然后打印新值。注意在main中重新打印a[3]时,它没有修改,因为各个数组元素是按值调用传递。
1 // Fig. 4.14: fig0414.cpp // Passing arrays and individual array elements to functions #include <iostream.h> #include <iomanip.h> void modifyArray( int [], int ); // appears strange void modifyElement( int ); int main(){ const int arraySize = 5; iht i, a[ arraySize ] = { 0, 1, 2, 3, 4 }; cout << "Effects of passing entire array call-by-reference:" << "\n\nThe values of the original array are:\n"; for( i=0;i< arraySize; i++ ) cout(<< setw( 3 ) << a[ i ]; cout << endl; // array a passed call-by-reference modifyArray( a, arraySize ); cout << "The values of the modified array are:\n"; for ( i = 0; i < arraySize; i++ ) cout << setw( 3 ) << a[ i ] ; cout << "\n\n\n" << "Effects of passing array element call-by-value:" <<"\n\nThe value of a[3] is "<< a[3] <<'\n'; modifyElement( a[ 3 ] ); cout << "The value of a[ 3 ] is "<< a[ 3 ] << endl; return 0; } void modifyArray( int b[ ], int sizeofArray ){ for ( int j = 0; j < sizeofArray; j++ ) b[ j ] *= 2; } void modifyElement( int e ){ cout << "Value in modifyElement is" <<(e *= 2 ) << endl; }
输出结果:
Effects of passing entire array call-by-Value: The values of the original array are: 0 1 2 3 4 The values of the modified array are: 0 2 4 6 8 Effects of passing array element call-by-value: The value of a[3] is 6 Value in modifyElement is 12 The value of a[3] is 6
图 4.14 向函数传递数组和数组元素
有时程序中的函数不能修改数组元素。由于总是模拟按引用调用传递数组.因此数组中数值的修改很难控制。C++ 提供类型限定符 const,可以防止修改函数中的数组值。数组参数前面加上const限定符时,数组元素成为函数体中的常量,要在函数体中修改数组元素会造成语法错误。这样,程序员就可以纠正程序,使其不修改数组元素。
图 4.15 演示了 const 限定符。函数 tryToModifyArray 定义参数 const int b[],指定数组b为常量,不能修改。函数想修改数组元素会造成语法错误 Canot modify const object。const 限定符将在第7章再次介绍。
// Fig. 4.15: fig04_lS.cpp // Demonstrating the const type qualifier #include <iostream.h> void tryToModifyArray( const int [] ); int main(){ int a[] = {10,20,30}; tryToModifyArray( a ); cout << a[ 0 ] << ' ' << a[ 1 ] << ' ' << a[ 2 ] << '\n'; return 0; } void tryToModifyArray( const int b[] ){ b[ 0 ] /= 2; // error b[ 1 ] /= 2; // error b[ 2 ] /= 2; // error }
输出结果:
Compiling FIG04 15.CPP: Error FIG04_iS.CPP 18: Canot modify a const object Error FIG04_iS.CPP 19: Canot modify a const object Error FIG04 15.CPP 20: Canot modify a const object Warning FIG04_15,CPP 21: Parameter 'b' is never used
图 4.15 演示 const 限定符
常见编程错误 4.10
忘记数组按引用传递以至于修改数组可能造成逻辑错误。
软件工程视点 4.3
const 限定符可以用于函数定义中的数组参数,防止函数体中修改原数组,这是最低权限原则的另一个例子。函数不能提供修改数姐的功能,除非确实有必要。