数组名作为函数参数
概念说明
当一个数组名作为参数传递给一个函数时,数组名就是指向数组第一个元素的指针,此时传递给该函数的是该指针的一份拷贝。
函数如果对数组名进行了下标引用(即在函数中使用了该形参访问或修改数组),实际是对该指针执行了间接访问操作,通过这种间接访问,被调函数可以访问和修改主调函数中的数组元素。
这其实还牵扯到另外一个知识,如果想将一个数组传给另外一个数组,不能直接将数组名传过去,也不能对数组名使用++操作。见博客《数组-高级篇》和《指针与数组》,数组给数组赋值,数组初始化,见博客《数组-应用篇》。
“传值调用”
所有函数的参数都是通过传值方式进行的,但数组名参数使用却像是通过传址调用传递的,这只是现象表现,但实际上它仍然是传值调用。作为参数的数组名其实是个常量指针,下标引用实际执行的就是间接访问。
数组名的传值调用行为表现传递给函数的是参数的一份拷贝(指向数组起始位置的指针的拷贝),所以函数可以自由地操作它的指针形参,而不必担心会修改对应的作为实参的指针。你改变不了这个指针本身的值,因为它只是一个拷贝,如果想要修改它就要用到指针的指针。
-
1.传址调用是通过传递一个指向所需元素的指针,然后在函数中对该指针执行间接访问操作实现对数据的访问。
2.所有的参数都是通过传值方式传递的。当然,如果你传递了一个指向某个变量的指针,而函数对该指针执行了间接访问操作,那么被调函数就可以修改那个变量。
3.数组名作为参数时是类似的道理。这个参数(指针)实际上是通过传值方式传递的,函数得到的是该指针的一份拷贝,它可以被修改,但调用程序所传递的实参并不受影响。
4.无论被调函数对参数(指针)如何进行修改,都不会修改调用程序的指针实参本身(但可能修改它所指向的内容)。
指针表示法形参与数组表示法形参
这里有一个有趣的问题。如果你想把一个数组名参数传递给函数,正确的函数形参应该是怎样的?它是应该声明为一个指针还是一个数组?
如上文所述,调用函数时实际传递的是一个指针,所以被调函数的形参实际上是个指针。但是,编译器也接受数组形式的函数形参。因此,下面两个函数原型是相等的:
int strlen( char *string );
int strlen( char string[]);
这两个声明确实相等,但只是在当前相应的上下文环境中。如果它们出现在别处,就可能完全不同。
对于数组形参,你可以使用任何一种形式的声明。 你可以使用任何一种声明,但哪个“更加准确”?
答案是指针。因为实参实际上是个指针,而不是数组。
这就是为什么函数原型中的一维数组形参无需写明它的元素数目,
因为函数并不为数组参数分配内存空间。形参只是一个指针,它指向的是已经在其他地方分配好内存的空间。
这解释了为什么数组形参可以与任何长度的数组匹配——它实际传递的只是指向数组第1个元素的指针。
另一方面,这种实现方法使函数无法知道数组的长度。如果函数需要知道数组的长度,它必须作为一个显式的参数传递给函数。
**只有在函数原型或函数引用 ,才可以使用int data_p[] 代替 int data_p **
int data_p[] 代替 int* data_p都表示data_p是一个指向int型的指针,。但是int
data_p[]只能用于声明形式参数。 使用int
data_p[]可以让程序员知道,指针data_p指向的不仅仅是一个int类型的值,还是int类型的数组。
声明函数数组形参的形式
1.因为数组名是该数组首元素的地址,,作为主调函数要春递给被调函数的实参-数组名,要求形参是一个能够与其匹配的指针。此时,C编译器会把int data_p[] 和 int* data_p解释成一样的。
即data_p是指向int型的指针。,由于函数原型可以省略参数名,所以以下四种原型是等价的
int fun_data_ctrl(int* data_p,int num);
int fun_data_ctrl(int* ,int );
int fun_data_ctrl(int data_p[],int num);
int fun_data_ctrl(int [],int );
- 函数的定义中不能省略参数名
int fun_data_ctrl(int* data_p,int num)
{
//功能代码
}
与上面的定义形式等价
int fun_data_ctrl(int data_p[]_p,int num);
{
//功能代码
}