您的位置 首页 > 数码极客

【数组成员引用下标超出定义范围】C++数组名的上下文(指向整个数组空间或首元素)和类型信息

在c或c中,数组是通过指针实现的,但n维数组与n级指针不相同。

n维数组定义时是包含有长度信息的,n级指针则没有(只有一级指针能直接表示出指向地址所表示的内存空间的长度,但超过一级的指针则不行)。

n维数组通过下标符号“[]”实现降维的解引用,而n级指针通过解引用符号“*”来实现,两者都能通过n次解引用后指向最终的内存空间最小元素的值(下标符号“[]”和解引用符号“*”在一定上下文中有一定的等价性)。需要注意的是,n维数组在一定的上下文中会转变为指针,转变的指针指向的是数组首元素的地址,并要求能保留首元素的长度信息,如:

int arr[2][3][4] = {24,1,2,3,4,5,6,7,8,9,10,11, 12,13,14,15,16,17,18,19,20,21,22,23}; int (*p)[3][4] = arr; // p的类型是int*[3][4],可以看到“[]”和“*”的等价性 cout<<(arr[0][0][0] == ***p)<<endl; // 1 cout<<(***p == sizeof(arr)/sizeof(***arr))<<endl; // 1

在C++中,因为长度信息的模糊性,编程时一般尽量避免直接使用原生数组和指针,而是推荐RAII机制(封装成类,包含长度信息并能确保数据的初始化和析构)和使用STL的vector或list。

在上面的实例中,看到了作为数组名的arr有不同的上下文,有时候表示整个数组的内存空间,有时表示的是数组首元素的内存地址及对应存储空间(此时转变为指针,指针本身代表了数组的第一维,其它维用来表示第一维的长度)。这里要明白多维数组的数组首元素表示的是首维对应的数据类型信息,如int arr[2][3][4]的首元素是&arr[0],对应的类型信息是int[3][4],所以有:

int (*p)[3][4] = arr; // p的类型是int*[3][4] int (*p2)[3][4] = &arr[0]; int (*p3)[4] = &arr[0][0]; cout<<**(p3+3)<<endl; // 12

为什么会出现数组名不同的上下文?因为数组是通过指针变量来实现的,数组名是一个特殊的指针变量,数组的下标引用是指针算术运算的“语法糖”,指针的算术运算是要求其指向的对象具有长度信息的,这样,指针的移动(偏移)才能正常进行。p3指向首地址,p3指向的类型信息是int[4],所以p3+3是要偏移12个int的字节长度。

变量声明的数据类型除了其定义的操作以外,还表明了其指向的内存空间的长度,如:

int i; // 长度为sizeof(i)

数组的数组名是基地址,既然做为地址的基准,自然不要随便变更为妥,所以其有常量的性质。定义后不能再用做左值。如:

int arr[3][4]; // 长度为sizeof(arr)

arr的类型是int,但完整的类型信息是int[3][4],其长度为sizeof(arr) = sizeof(int)*3*4。但数组类型的维数和长度却是在定义时确定的,arr[0]的类型信息是int[4],arr[0][0]的类型是int。与结构体不同的是,结构体通过类型定义以及数据对齐,有确定的长度信息,如:

struct Stu{ int id; char name[12]; int grades; }; struct Stu stu; // 与数组不同的是,在定义后还可以用做左值

stu的类型是struct Stu,长度是确定的,显然与同样作为复合类型的数组的长度信息的确定完全不同。

指针的算术运算,描述的是指针按其数据类型的长度的跳跃移动,如:

int * p; p += 5; // 指针p移动5个int,sizeof(int)*5个字节

1 数组名表示整个数组空间

如有数组:

int arr[2][3][4] = {24,1,2,3,4,5,6,7,8,9,10,11, 12,13,14,15,16,17,18,19,20,21,22,23}; int (*p)[3][4] = arr; // p的类型是int*[3][4]

1.1 声明时,arr自然表示的是整个数组空间。

1.2 使用sizeof运算符时

sizeof(arr)表示的是整个数组空间的字长长度。

int (*p2)[3][4] = &arr[0]; cout<<sizeof(arr)<<endl; // 96 cout<<sizeof(p2)<<endl; // 4,此时是一个指针的长度,丧失了长度信息int[3][4]

1.3 使用&运算符时

cout<<(int)(&arr+1)-(int)arr<<endl; //96 cout<<(int)(p+1)-(int)p<<endl; // 48

2 转换为指向数组首元素的指针常量

arr的首元素的类型信息是int[4][5],有:

int (*p)[4][5] = arr; p++; // 表示移动sizeof(int)*4*5个字节

arr[0]的首元素的类型信息是int[5],有:

int (*p)[5] = arr[0]; p++; // 表示移动sizeof(int)*5个字节

arr[0][0]的首元素的类型信息是int,有:

int *p = arr[0][0]; p++; // 表示移动sizeof(int)个字节

3 数组名的使用

对于数组:

int arr[2][3][4] = {24,1,2,3,4,5,6,7,8,9,10,11, 12,13,14,15,16,17,18,19,20,21,22,23};

arr声明后,只有一种情况可以用做左值,就是arr[i][j][k],其中i、j、k分别要小于其维度。arr[i][j][k]表示取值或最终内容的引用,用做左值时表示更新,用做右值是表示取值。

其它情况,只能用在右值表达式中,并转换为指向数组首元素的指针常量。

总结一下:

-End-

责任编辑: 鲁达

1.内容基于多重复合算法人工智能语言模型创作,旨在以深度学习研究为目的传播信息知识,内容观点与本网站无关,反馈举报请
2.仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证;
3.本站属于非营利性站点无毒无广告,请读者放心使用!

“数组成员引用下标超出定义范围”边界阅读