#include <stdio.h>
int main()
{
int a[3][2]={10,11,12,13,14,15};
printf("%d %d",a[2][-2],a[2][-1]);
}
我得到输出为12,13,但我不明白为什么? 指针是否存在多维数组,如[1]与单个维度中的*(a + 1)相同?
答案 0 :(得分:2)
您的示例是官方未定义的行为。我将参考C标准:
连续的下标运算符指定多维数组对象的元素。 如果E是具有尺寸i x j x的n维阵列(n> = 2)。 。 。 x k,然后E(用作除左值之外)被转换为指向尺寸为j x的(n-1)维数组的指针。 。 。 x k。 如果将unary *运算符显式地应用于此指针,或者由于下标而隐式应用于该指针,则结果是引用的(n-1)维数组,如果将其用作除非之外的数组,则该数组本身将转换为指针左值即可。由此得出,数组以行主要顺序存储(最后一个下标变化最快)。
当向指针添加或从指针中减去具有整数类型的表达式时,结果具有指针操作数的类型。 如果指针操作数指向数组对象的元素,并且数组足够大,则结果指向与原始元素偏移的元素,使得结果元素和原始数组元素的下标的差异等于整数表达式。换句话说,如果表达式P指向数组对象的第i个元素,则表达式(P)+ N(等效地,N +(P))和(P)-N(其中N具有值n)分别指向数组对象的第i + n和第th个元素,只要它们存在即可。此外,如果表达式P指向数组对象的最后一个元素,则表达式(P)+1指向一个超过数组对象的最后一个元素,如果表达式Q指向一个超过数组对象的最后一个元素,表达式(Q)-1指向数组对象的最后一个元素。 如果指针操作数和结果都指向同一个数组对象的元素,或者指向数组对象的最后一个元素,则评估不应产生溢出;否则,行为未定义。如果结果指向数组对象的最后一个元素之后,则不应将其用作已计算的一元*运算符的操作数。
上面的重点是我的。表达式a[2][-2]
在上述两段中具有很多意义。根据第一段a[2]
将引用一个数组,特别是int[2]
中包含的第三个a
。此时,任何应用的下标运算符都需要对这个2个整数数组有效。
由于[-2]
现在应用于int[2]
,因此生成的指针算法超出了它应用的聚合,并且根据第二段,是未定义的行为。
话虽如此,我所知道的大部分实现都是人们所期待的,而其他答案则记录了你获得这些价值的方式。
答案 1 :(得分:1)
声明为
的二维数组int a[3][2] = { 10, 11, 12, 13, 14, 15 };
您可以将其解释为声明为
的一维数组int a[3 * 2] = { 10, 11, 12, 13, 14, 15 };
在二维数组的索引和一维数组的索引之间存在以下关系
a[i][j]
对应a[2 * i + j]
因此,二维数组的元素a[2][-2]
对应于a[2 * 2 - 2]
的一维数组的元素a[2]
a[2]
的值等于12
。并且二维数组的元素a[2][-1]
的值对应于到a[2 * 2 - 1]
等于13的一维数组的元素a[3]
的值。
反向计算。如果你有一个一维数组的元素a [i]那么它有一个二维数组的以下对应元素
a[i / nCols][i % nCols ]
其中nCols
是二维数组中的列数。
答案 2 :(得分:0)
我认为这不是未定义的行为;第6.5.2.1节中的C standard数组下标描述了如何解释数组下标。我将那里定义的段落应用于OP提出的示例:
int a[3][2];
这里a
是一个3×2的整数数组;更确切地说,a
是一个包含三个元素对象的数组,每个元素对象都是两个整数的数组。在表达式a[i]
中,它等同于(*((a)+(i)))
,a
首先转换为指向两个整数的初始数组的指针。然后根据i
的类型调整a
,其概念上需要将i
乘以指针所指向的对象的大小,即两个int对象的数组。然后,表达式a[i][j]
等同于(*(*((a)+(i)))+(j))
,其中j
在概念上乘以int的大小。
就内存地址而言,假设4
为sizeof(int)
,这意味着a + (i*sizeof(int[2])) + (j*sizeof(int))
;对于i=2
和j=-2
,这意味着a + 16 - 8
,即a + 2*sizeof(int)
。在int a[3][2]
的内存布局中,取消引用此内存地址可实现12
。
答案 3 :(得分:0)
您的数组如下所示:
10 | 11
---+---
12 | 13
---+---
14 | 15
但在记忆中它看起来像这样:
10
--
11
--
.
.
.
--
15
这意味着如果你想访问元素a[1][1]
(= 13),你不必访问1 + 1 = 2nd元素,因为你必须跳过包含2个元素的整个第一行,所以你必须访问1 * 2 + 1 = 3rd(10将是第0个)元素。
所以如果你有一个数组:
int a[nRows][nCols];
访问a[i][j]
将如下所示:
*(a + nCols * i + j) = val;
所以在你的例子中它将是:
*(a + 2 * 2 + (-2)) = *(a + 2)
和
*(a + 2 * 2 + (-1)) = *(a + 3)