1 数组声明时的[]符号及数组名
下面声明一个5个元素的数组并同时初始化:
int a[5]={1,2,3,4,5};//有初始化时,数组第一维的长度可以省略,编译器可以由初始化的元素个数推出第一维的长度
数组中的数组名在代码的上下文中C++编译器有两种不同的解释:
I 解释为数组首元素的地址,如:
cout<<*a; //1 cout<<a[2]; //3,编译器会将a[2]解释为*(a+2),表示相对于首元素偏移两个位置的值
下标符号[]在上下文中也有不同的解释,在数组声明和定义(同时有类型声明)时,[]内的数字表示元素个数,在元素引用(不会有类型声明)时,[]内的数值表示位置偏移值。
II 表示数组长度,如:
cout<<sizeof(a); //5
数组a在内存中是如何映像的呢?
5个元素在内存中连续存储,因为是相同类型,各元素占用的内存空间长度也是相同的。此时数组名a表示数组首元素的地址。
此时,以下两条语句都是输出相同地址值:
cout<<a; //0012FF34 cout<<&a; //0012FF34
&a表示整个数组的首地址,与表示数组首元素的地址的a有什么区别呢?
cout<<a+1;//0012FF38 cout<<&a+1;//0012FF48
上述代码“+1”的操作表示按加号前面地址所表示的字节大小来移动一个单位所表示的地址,a+1表示移动一个sizeof(a[0]),也就是4个字节(sizeof(int)),&a+1表示移动sizeof(a),也就是5*4个字节。
可以理解一下以下代码:
(int*)(&a+1);
表示一个指向数组a最后一个元素之后的下一个内存单元开始的4个字节的内存单元的指针,类型是int。
2 类型声明时同时存在*和[]
如:
char* p[5]={"abc","de","fg","hij","k"}; cout<<p[2]; //fg
在char* p[5]中,*的优先级要低,char* p[5]最终表示的是一个数组,一个指针数组,数组中的元素是char*指针。
如果有()来提供*的优先级,如:
int a[2][2]={1,2,3,4}; int (*p)[2]; p=a; cout<<**(++p);//3
int (*p)[2];最终表示的是一个指针,一个指向数组的指针。
3 类型声明时同时存在*、[]和()
int(*fn[])(int,int);
先要分析括号内的部分,*的优先级低于[],所以它最终是一个数组,是一个数组元素是指针的数组,其中的指针是函数指针,所以最终它是一个函数指针数组。
基本思路是从内核开始层层外推,层层加定语。也就是:数组→指针数组→函数指针数组:数组元素是函数指针,指针是指向函数的指针。
4 对于数组下标的理解
上文说过,对于数组下标,C++编译器最终会解释为指针偏移后的解引用。如:
int a[5]={1,2,3,4,5};
int a[3] = 88; //编译器会将a[3]解释为*(a+3)
再看下面的代码:
int a[5]={11,12,13,14,15}; int b[5]={21,22,23,24,25}; int c[5]={31,32,33,34,35}; b[7]=777; b[-2]=222; cout<<a[2];//777 cout<<c[3];//222 cout<<*(int*)(&b+1);//11
在内存映像中,数组b夹在a和c中间,b的下标只要在[-5,5]中间偏移,程序编译都是可以通过的,当偏移超过自己的内存空间时,修改的是邻近数组的值。
-End-