1D数组衰减到指针,但2D数组​​不这样做,为什么?

时间:2013-03-08 14:11:08

标签: c++ c pointers

有人可以向我解释为什么技术上2D阵列实际上不会衰减到int**,而当你有一个单维数组时,你会把指针衰减到int*(如果你有{{1}的数组{1}})。

我理解指针衰减是什么,但我不知道为什么2D数组不会出现这种衰变?

对于刚接触C或C ++的人来说,这是一个常见的混淆,但我发现自己也常常绊倒它。

7 个答案:

答案 0 :(得分:6)

他们有不同的内存布局。 2D数组是一个连续的内存,而int**是一个指针数组。对于2D数组,位置的偏移量计算为rownum * collength + colnum(反之亦然,具体取决于标记行/列的方式)。这不适用于int**,它实际上产生两个内存读取;第一个获取列指针,然后读取与该存储器偏移的数据。

顺便提一下,这种不同的布局是必须在接受2D数组的函数中声明数组维度(除了最左边)之外的原因;否则编译器无法生成计算偏移量的代码。

以下是对int**的内存布局图片的尝试。左列是一个连续的指针数组,其中每个指针都包含一个连续内存和数据的地址。请注意,每列中的数据长度不必相同(尽管对于代码可读性而言可能不一定是好事):

int **array; 
int i, j;
int cntr = 1;
array = malloc( 3 * sizeof( int* ));
for ( i = 0; i < 3; i++ ) {
   array[i] = malloc( 4 * sizeof( int ));
   for ( j = 0; j < 4; j++ )
      array[i][j] = cntr++;
   }

给出这样的东西:

array ==> |addr1|  ==>  [1,2,3,4]
          |addr2|  ==>  [5,6,7,8]
          |addr3|  ==>  [9,10,11,12]

相比之下,这就是int[3][4]的布局。括号只显示每列数据的逻辑中断。整数值在内存中是连续的:

int array[3][4];
int i, j;
int cntr = 1;
for ( i = 0; i < 3; i++ )
   for ( j = 0; j < 4; j++ )
      array[i][j] = cntr++;

给出这样的东西:

array ==> [1,2,3,4][5,6,7,8][9,10,11,12]

答案 1 :(得分:4)

模式是“N元素数组T”类型的表达式将转换为“指向T的指针”类型的表达式。如果T是一个数组类型,那么你最终会得到一个指向数组的指针。给出像

这样的声明
T arr[M][N];

表达式arr具有“T的N元素数组的M元素数组”。除非它是sizeof_Alignof或一元&运算符的操作数,否则它将转换为类型为“指向{{1}的N元素数组的指针”的表达式}“或T

如果有疑问,请写出英文类型,例如“{元素数组的五元素数组的六元素数组T (*)[N]”,然后替换第一个“n元素数组”使用“指向”指针,给出“指向int的6元素数组的5元素数组的指针”:

int

答案 2 :(得分:3)

将二维数组转换为指向其第一个元素的指针的结果是指针,而不是数组。由于它不是数组,因此不会进一步转换。

答案 3 :(得分:2)

它确实腐烂了。类型为“T的数组”的表达式衰减为指向其第一个元素的指针。对于T的二维数组,第一个元素的类型数组为T。所以:

int arr[5];

在大多数情况下,arr都有“指向int”的类型。现在将相同的规则应用于二维数组:

int arr2[5][5];

此处,arr2具有类型“指向5 int数组的指针”。请注意,此对象的类型为T数组”,因此它不会进一步衰减。

答案 4 :(得分:1)

如果int* i == int i[5]而不是int** i == int i[5][5],那么人们常常会认为这有两个原因。首先,int**pointer to a pointer并且与数组的布局方式无关,其次,2D数组实际上是内部的1D数组,它的作用是将第二个数组并排放置。 / p>

这意味着在[2][2]数组内部正在创建一个[4]数组,当您尝试访问[0][0]时,该数组会被转换为[0],{{ 1}}将被翻译为[0][1][1]将被翻译为[1][0][2]将被翻译为[1][1]

示例:

[3]

这将打印125,因为我将#include <iostream> void Print(int* i, int element) { std::cout << i[element] << std::endl; } int main() { int i[5][5]; i[2][2] = 125; Print((i[0]), 12); } 设置为125并且将被视为等于[2][2],因为第一个12将占用0 - 4,[0]将占据5-10,依此类推。

答案 5 :(得分:1)

二维数组是数组的数组

在大多数情况下,“T数组”转换为“指向T的指针,因此”T[N]数组“将转换为”指向{的指针{1}}”。

由于

的内存表示
T[N]

完全不同
int arr[M][N];

int **dptr = malloc(M*sizeof *dptr); for(int i = 0; i < M; ++i) { dptr[i] = malloc(N*sizeof *dptr[i]); 甚至无法转换为int[M][N]

答案 6 :(得分:-1)

2D数组是一个连续的内存块。当它作为2D数组访问时,您需要行长度从列跳到列。指向指针的指针就是这样。如果第一个指针是指向第二维数组的指针数组,它可以模拟2D数组,但它与2D数组的连续内存块是完全不同的野兽。