[2] 数组和指针的前世今生 - 数组篇
来源:互联网 发布:大司马淘宝 编辑:程序博客网 时间:2024/05/23 01:16
本节主要说明为什么C语言要把数组形参作为指针传递给函数。
历史原因
- 效率。在C语言中,所有非数组形式的数据实参都是传值调用。然而,如果拷贝整个数组,无论是时间还是空间的开销都有可能非常大,而且在绝大多数情况下,其实并不需要整个数组的拷贝。
同样出于效率的目的,Pascal语言选择在传入函数的形参加入一个存储说明符,来说明是深拷贝还是浅拷贝。C语言则采用了另一种方法->即所有数组在作为参数传递的时候转换为指向数组首地址的指针,而其他参数则采用值传递,这样做就可以简化编译器,类似的,函数的返回值也不能是函数数组,只能是指向数组或者函数的指针。
还有一个例子是lint程序,处于效率的愿意,被C语言编译器排除在外。
数组参数传递
int func(int *p);int func(int p[]);int func(int p[200]);//可以用下列任何实参传入上面的三个函数int n;int *n;int n[10];
1、需要注意的是,在函数内部,根本无法判断传入的是一个数组,还是一个指针,是一个指向一个int的指针,还是一个指向很多int的指针,无法区分;因此在传递指针或者数组的时候,会同时传递一个长度信息(对于字符串则不需要,因为C语言中字符串结尾有’\0’作为标识,指针也不需要,可以用NULL做结尾)
2、如果非要把一个数组整体传递给函数,则需要把数组放在一个结构体中
3、函数内部无法用sizeof得到数组长度
4、因此,在声明一个函数的时候,你可以选择声明成什么形式,数组还是指针,不论选什么,对编译器都没有影响,有一些建议如下:
5、建议把所有声明都写成指针形式,和编译器保持一致;
6、但也有人觉得int a[]和int *a比较,前者更能突出表明了a内部有好几个元素的特征,提示函数会对其内部进行处理,见仁见智。
1个例外
有一样操作只能在指针里进行,不能对数组进行,就是修改它的值,数组名是不可修改的左值,代码如下:
func1(int *ptr){ ptr[1] = 3; *ptr = 3; ptr = array2;//正确}func2(int arr[]){ arr[1] = 3; *arr = 3; arr = array2;//正确,因为已经转化为指针}int array[100];int array2[100];int main(){ array[1] = 3; *array = 3; func1(array);//正确 func2(array2);//正确 array = array2;//错误}
多维数组\数组的数组
Ada语言标准明确说明数组的数组和多维数组是不一样的;
Pascal语言标准明确说明数组的数组和多维数组是一样的;
C语言里面只有一个别的语言成为数组的数组的形式,但C语言称它为多维数组
- 可以把[i][j][k]计算出相应的偏移量,使用[z]来引用数组,当然这不是一种值得推荐的方法
- C语言的数组就是一种向量,也就是某种对象的一维数组,但是这种对象可以是另一个数组
char carrot[10][20];typedef char vegetable[20];vegetable carrot[10];//注意此时是char carrot[10][20]不是[20][10]carrot[i][j];//无论上述哪种形式,编译器都会解析成*(*(carrot+i)+j)
多维数组的分解
正常情况下,赋值发生在两个相同的类型之间,如int和int,double和double等。但是上图中可以看出,“数组的数组的数组”中的每一个单独的数组都可以看作是一个指针,因为在表达式中的数组名会被编译器当作指向第一个元素的指针,所以不能把数组赋值给另一个数组,因为数组作为一个整体不能作为赋值的对象。
- 指针运算也大不相同
- r++;和t++;两个语句都会使得r和t指向它们各自的下一个元素(两者所指向的元素本身就是数组),所以它们的步长是不相同的,p走15,r走5,t走1.
int apricot[2][3][5];int (*p)[3][5] = apricot;int (*r)[5] = apricot[0];int (*t) = apricot[0][0];printf("%x,%x,%x",p,r,t);p++;r++;t++;printf("%x,%x,%x",p,r,t);
多维数组的内存布局
- C语言的多维数组中,最右侧的下标是最先变化的,这个约定被称为“行主序”,由于“行/列主序”这个术语只适用于二维数组,所以更确切的说法是“最右侧的下标先变化”,绝大部分语言都是行主序,Fortran例外
数组初始化
float banana[5] = {0.0,1.0,2.0,3.0,4.0};float honeydew[] = {0.0,1.0,2.0,3.0,4.0};short cantaloupe[2][5] = { {10,12,3,4,-5}, {31,32,6,0,-5},//最后的,可以省略};int rhubarb[][3] = {{0,0,0},{1,1,1,},}//最后的,可以省略
- 一维数组约定如果没有确切值,则按照花括号中的个数来确定长度
- 只能够在对数组声明的时候对它初始化,只所以存在这个限制,并没有什么特别的理由,属于我就这样,爱咋咋地
- 多维数组通过嵌套的花括号初始化,最后的‘,’可以省略,同时也可以省略第一维的长度
- 如果数组的长度比所提供的初始化的值要大,剩余的初始化为0,如果是指针,初始化为NULL,如果是float,初始化为0.0
下面是一种初始化二维字符串的方法,注意字符串常量可以用做数组初始值,编译器会正确的把各个字符存储在数组中的地址,第二句初始化,要记住只要字符串常量才可以初始化指针数组,否则就要malloc。所以第三句初始化无法编译通过。
char vegetables[][9] = { "beet","bearley","basil","broccoli","beans"};char *vegetables[] = { "beet","bearley","basil","broccoli","beans"};int *weights[] = { {1,2,3,4,5},{6,7},{8,9,10}};//ERROR
非要初始化上述第三句,可以按照如下方式:
int row_1[] = {1,2,3,4,5};int row_2[] = {6,7};int row_3[] = {8,9,10};int *weight[]={row_1,row_2,row_3};
char ga[] = "abcdef";void print_array(char ca[]){ printf("address of array param = %#x \n",&ca); printf("address (ca[0]) = %#x \n",&(ca[0])); printf("address (ca[1]) = %#x \n",&(ca[1])); printf("++ca = %#x \n",++ca);}void print_pointer(char *pa){ printf("address of ptr param = %#x \n",&pa); printf("address (pa[0]) = %#x \n",&(pa[0])); printf("address (pa[1]) = %#x \n",&(pa[1])); printf("++pa = %#x \n",++pa);}int main(){ printf("address of gloable array = %#x \n",&ga); printf("address (ga[0]) = %#x \n",&(ga[0])); printf("address (ga[1]) = %#x \n",&(ga[1])); print_array(ga); print_pointer(ga); return 0;}
Reference
C专家编程
- [2] 数组和指针的前世今生 - 数组篇
- [1] 数组和指针的前世今生 - 数组篇
- [3] 数组和指针的前世今生 - 指针篇
- [4] 数组和指针的前世今生 - 指针篇
- [0] 数组和指针的前世今生
- 指针与数组的前世今生
- HTTP/2的前世今生
- 还原java的今生和前世!
- svn的前世今生和安装使用
- ORA-19706和_external_scn_rejection_threshold_hours的前世今生
- 软件需求说明的前世和今生
- 堆和栈的前世与今生
- 接口和抽象类的前世今生
- Promise的前世今生和妙用技巧
- C#委托和事件的前世今生
- servlet、jsp和框架的前世今生
- EmguCV 的前世今生和扩充
- 【转】我和Linux的前世今生
- C++中的动态绑定
- eclipse中,如何正行上下移动
- egrep用法
- Collection--Set--hashSet
- 如何在eclipse中查看源码?
- [2] 数组和指针的前世今生 - 数组篇
- 实验四 看电视
- Redis-util 转换函数
- 【MySQL】统一控制台-pma-PHP编码!解决中文乱码问题
- 浅析代理模式
- [有上下界的网络流]
- c++实验2-标准体重
- 联想昭阳E47A无线网卡指示灯不亮,搜不到无线信号,解决办法
- BZOJ 1856 SCOI 2010 字符串 卡特兰数