一次关于数组作为函数参数的较真
2017-11-11 11:55
441 查看
有关形参和实参
example1
void fun( int x ) { x = 5; } void main() { int a = 3; fun( a ); }
对C语言有一定了解的人应该都知道,通过调用fun并不能把a的值改变为5.(如果这一点你都不明白,请不要继续往下阅读了)。因为C语言并不是真正的把a传入fun函数,只是复制了a的值传给局部变量x(值传递,形式参数)。
example2
再看一个例子void fun( int x[] ) { x[1] = 1; } void main() { int a[2] = { 0, 0 }; fun( a ); printf( "%d", a[1] ); }
然而这段代码最后的输出却是1。为什么数组传入后函数里对x的改变就可以改变a的值呢?因为在C语言中,数组作为参数传递的时候,传入的是数组首地址(相当于传入一个指向数组的指针),而不是采用和普通整形变量一样的值传递(拷贝一份数值)。
验证
example1
#include <stdio.h> #include <time.h> int a[5] = {0}; int b[5000] = {0}; int c[500000] = {0}; void fun( int x[] ) { return ; } void main() { int i, j; time_t start_time, end_time; //记录起始时间、结束时间 //test 1 ----------------------------------------------------------------- start_time = clock(); for( i = 0; i < 10000; i++ ) for( j = 0; j < 500; j++ ) //循环5000000次 fun( a ); end_time = clock(); printf( "Array length : 5 Use time : %ld \n", end_time - start_time ); //test 2 ----------------------------------------------------------------- start_time = clock(); for( i = 0; i < 10000; i++ ) for( j = 0; j < 500; j++ ) fun( b ); end_time = clock(); printf( "Array length : 5000 Use time : %ld \n", end_time - start_time ); //test 3 ----------------------------------------------------------------- start_time = clock(); for( i = 0; i < 10000; i++ ) for( j = 0; j < 500; j++ ) fun( c ); end_time = clock(); printf( "Array length : 500000 Use time : %ld \n", end_time - start_time ); }
上面的程序中定义了三个长度不同的数组abc,函数fun的功能很简单,传入一个数组,然后什么都不干直接返回。如果C语言采用值传递的方法,那么显而易见的test2和test3的运行的时间应该比test1大的多。让我们看看输出。
多运行几次,你会发现,三段程序的执行时间几乎一致。这成功的验证了我们的想法。同样的,你不妨试试定义成50000*50000的二维数组,程序的效率和你传入一个长度为5的数组是一样的。TRY IT!
example2
void fun( int x[100] ) { printf("x size : %d\n",sizeof(x) ); } void main() { int a[100]; printf("a size : %d\n",sizeof(a)); fun(a); }
对于a的大小为400大家应该不陌生,4(int型)*100(数组长度)=400
但是同样的int x[100],x的大小就只有8(long int型,指针大小)。说明了并没有为x开辟100个单元空间用来存储参数,x只是一个指针。
example3
手头有数据结构课本的学弟学妹不妨翻到P220看一下Figure7.2。函数的参数列表有两个 Element_Type A[]和int N。其中A[]是待排序的数组。然而额外传递了一个等于A长度的参数N。说明在参数传递的过程中,数组的长度信息丢失了,因此不得不通过int N传入。
一个问题
既然无论多大的数组,都只是传入首地址进函数,就产生了一个问题:数组的长度信息丢失了!就好比你只知道每个班级第一个同学的学号,而无法知道整个班级的人数。因此,你必须显式地定义一些参数的长度。尝试定义这样一个函数
void fun( int x[][] ) { return ; }
编译器会丢给你一个ERROR。提示 array type has incomplete element type(数组类型含有不完整的元素类型)。因为,编译器不知道这个二维数组是多大的!
所以你只能写成
void fun( int x[][5] ) { return ; }
另一个问题
你可能要问,为什么第一个方括号里可以不用填写数字(即不需要告诉编译器你二维数组的行数),同时,以下的定义是合法的void fun( int x[] ) { return ; }
并且你可以在这个函数中使用x[3],改变原参数的数值。
让我们了解一下数组是如何定位元素的。对于一维数组x[3],定位过程是 x(首地址)+3(偏移量),这个偏移量是我们给定的,可以是任意值(甚至允许你越界访问),同时x已知,因此对于编译器来说,信息已经足够。
对于二维数组x[3][4],定位过程是x()+3(行数)* 每行数组的元素数 + 4(偏移量)
此处x已知,3和4已经给出。对于编译器来说,每行数组的元素数是未知的,因此必须显示给定。
试一试
int a[3][3] = {0}; void fun( int x[][4] ) //CHEAT! { x[1][1] = 1; } void main() { int i, j; fun( a ); for( i = 0; i < 3; i++ ) //打印数组 { for( j = 0; j < 3; j++ ) printf( "%d ", a[i][j] ); printf("\n"); } }
在这里,我们对编译器做了一个“欺骗”,我们欺骗它说,我们将传入一个每行长度为4的数组x进去。但是实际上传入的a是一个3行3列的数组。
看看结果
当我们改变x[1][1]时。编译器傻傻得以为一行真的有四个元素
把a[0][0]到a[0][2]和a[1][0]当作了第一行的四个元素并跳过
把a[1][1]当作x[1][0],把a[1][2]当作了x[1][1]进行修改
总结
C语言中,数组作为参数,总是只能传入指针,这是C语言一个伟大的发明(他允许你通过函数操作任何一个大数组而不影响效率)。但是也带来了一些不便(你必须显式的指定数组长度,或者在函数的传入参数列表中加入一个变量传递数组长度信息)相关文章推荐
- 关于 Numpy 数组作为函数参数的一个小问题
- 关于将数组作为参数传递给函数的简单使用
- c++中关于数组作为函数参数的几种方法
- c++中关于数组作为函数参数并传递数组元素个数的几种有效方法的讨论
- 2.关于结构体以及结构体数组作为函数入口参数的方法(学习笔记)
- 关于作为函数参数的多维数组的声明、定义和调用
- c++中关于数组作为函数参数并传递数组元素个数的几种有效方法的讨论
- C++学习笔记(四)——关于数组作为函数参数的值传递和引用传递
- c++中关于数组作为函数参数并传递数组元素个数的几种有效方法的讨论
- 数组作为函数的参数传递
- C语言:数组名作为函数参数
- 关于js深入理解:json作为函数参数 类似于jQuery的post函数,具有url,data ,和回调函数
- 数组作为函数参数
- 048.Array Ele Arg 数组的元素作为函数的参数
- C语言中如何将数组作为函数参数传递
- 《IOS_作业C语言》高级指针(结构体指针、结构体数组指针、结构体数组指针,作为函数的参数使用、预编译指令)
- 鸡啄米:C++编程入门系列之二十六(数组、指针和字符串:数组的存储与初始化、对象数组、数组作为函数参数)
- java 11:数组作为函数参数,数组做为函数返回值
- 【C语言】将数组作为函数的参数
- 数组作为函数参数 自动转换为同类型指针