函数参数的传递(数组形参)

时间:2022-04-07 19:09:23

四、数组形参

1、数组形参的定义

如果要编写一个函数,输出int型数组的内容,可用下面三种方式指定数组形参:

void printValues(int *){ /*...........*/ }

void printValues(int [ ]){ /*........*/ }

void printValues( int [10]) { /*..........*/ }

虽然不能直接传递数组,但是函数的形参可以写成数组的形式。上面的三种定义是等价的,形参类型都是int *。

将数组形参直接定义为指针要比使用数组语法定义更好,定义为指针明确的表示 :函数操纵的是指向数组元素的指针,而不是数组本身,因此函数忽略数组长度,但形参定义中如果包含了数组的长度则特别容易引起误解。

2、形参的长度会引起误解

编译器忽略为任何数组形参指定指定的长度。根据数组的长度(权且这样说),可将函数printValues编写为:

  void printValues( const int a[10])

{

  //this code assumes array has 10 elements

  //disaster if argument has fewer than 10 elements

  for( size_t i =0 ; i != 10 ; ++i)

    {

            cout<<ia[ i ]<< endl ;

   }

}

尽管上述代码假定所传递的数组至少含有10个元素,但C++语言没有任何机制强制实现这个假设。下面的调用都是合法的:

   int main()

  {

      int i =0 , j[ 2 ] ={0,1};

      printValues(&i);  // ok : &i is int * ; probable run-time error

      printValues( j );  // ok : j is converted to pointer to 0th

                                  // element ; argument has type int * ;

                                 // probable run-time error   

         return 0;

}

虽然编译没有问题,但是这两个调用都是错误的,可能导致运行失败。在这两个调用中,由于函数printValues假设传递进来的数组至少含有10个元素,因此造成数组内存的越界访问。程序的执行可能产生错误的输出,也可能崩溃。

注解:当编译器检查数组形参关联的实参时,它只会检查实参是不是指针、指针类型和数组元素的类型是否匹配,而不会检查数组的长度。

3、数组实参

和其他类型一样,数组形参可定义为引用或非引用类型。

在传递数组时,实参是指向数组第一个元素的指针,形参复制的是这个指针的值,而不是数组元素本身。函数操纵的是指针的副本因此不会修改实参指针的值,然而,函数可通过指针改变他所指向的数组元素的值。

通过指针形参做的任何改变都在修改数组元素本身。(不需要修改数组形参的元素时,函数应该将形参定义为指向const对象的指针(const type *valuename))

void f( const int *){ /*......*/ }

3、通过引用传递数组((&arr)[ N ])

数组形参可声明为数组的引用。如果形参是数组的引用,编译器不会将数组实参转化为指针,而是传递数组的引用本身。在这种情况下,数组大小成为形参和实参类型的一部分,编译器检查数组实参的大小与形参的大小是否匹配:

void printValues( int (&arr)[10]) { /*......*/ } // &arr两边的圆括号是必需的,因为下标操作符具有更高的优先级

int main()

{

     int k[10] = {0,1,2,3,4,5,6,7,8,9};

     printValues(k); //ok : argument is an array of 10 ints

    int i=0, j[2]={1,2};

    int a[12]={1,2,3,4,5,6,7,8,9,0,11,12};

    printValues( &i ); //error :argument is not an array of 10 ints

    printValues( j );  //error : argument is not an array of 10 ints

    printValues( a ); //error : argument is not an array of 10 ints

}

这个版本的printValues函数只严格接受含有10个int型数值的数组,由于形参是引用,在函数体中依赖数组的大小的安全的。

4、多维数组的传递

5、传递给函数的数组的处理

任何处理数组的程序都要确保程序程序停留在数组的边界内。

有三种常见的编程技巧确保函数的操作不超出数组实参的边界。第一种方法在数组本身放置一个标记来检测数组的结束。C风格字符串就是采用这种方法的一个例子,它是一种字符数组,并且以空字符null作为结束的标记。

1.使用标准库规范

    第二种方法是传递指向数组第一个和最后一个元素的的下一个位置 的指针:

     void printValues( const int *beg ,const int *end )

     {

       while ( beg != end ){

                            cout<< *beg++ << endl;

             }

     }

      int main()

     { 

        int j[2] = { 0,1};

        // ok: j is converted to pointer to 0th element in j

        //  j + 2 refers one past the end of j

        printValues( j, j + 2 );

        return 0; 

      } 

    调用这个版本的函数需要传递两个指针:一个指向要输出的第一个元素,另一个则指向最后一个元素的下一个位置,要正确计算指针,使它们标记一段有效的元素范围,程序就会安全。

2.显示传递表示数组大小的形参

第三种方法是将第二个形参定义为表示数组的大小,这种用法在C程序和标准化之前的程序中十分普遍。

void printValues( const int ia[], size_t size)

{

   for ( size_t i = 0; i != size; ++i){

                 cout<< ia[ i ]<< endl;

           }

}

int main( )

{

    int j[ ] = {0,1} ;  // int array of size 2

    printValues( j , sizeof( j )/sizeof( *j ));

    return 0;

}