@dbush写了一个好的、正确的答案,解释了什么是保证和允许的。简而言之:是连续的,但仍然没有任何保证可以让您可靠地访问任何(子)数组越界。任何指向项的指针都需要指向有效的数组,才能在其上使用指针算术或[]
运算符。
这个答案添加了一些可能的解决方法来解决这个问题。
一个解决方法是使用一个union
和一个2D数组和1D数组之间的“类型游戏”:
#include <stdio.h>
typedef union
{
int arr_2d [3][4];
int arr_1d [3*4];
} arr_t;
int main (void)
{
arr_t a =
{
.arr_2d =
{
{ 1, 2, 3, 4},
{ 5, 6, 7, 8},
{9, 10, 11, 12}
}
};
printf("%d %d\n", a.arr_1d[6], a.arr_1d[(1*4)+2]);
return 0;
}
另一个通用的解决方法是,可以使用字符类型来检查 C 中的任何变量作为原始数据块,这样你可以将整个变量视为字节数组。
#include <stdio.h>
int main (void){
int a[3][4] = {{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}};
unsigned char* ptr = (unsigned char*)a;
int x = *(int*)&ptr[sizeof(int)*6];
int y = *(int*)&ptr[sizeof(int[4])*1 + sizeof(int)*2];
printf("%d %d\n", x, y);
return 0;
}
或者如果你是一个饱受争议的宏迷,可以重写上一个例子(但并不推荐):
#include <stdio.h>
#define GET_ITEM(arr, x, y) \
_Generic( **(arr), \
int: *(int*) &((unsigned char*)(arr))[sizeof(*arr)*(x) + sizeof(int)*(y)] )
int main (void){
int a[3][4] = {{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}};
unsigned char* ptr = (unsigned char*)a;
printf("%d %d\n", GET_ITEM(a,0,6), GET_ITEM(a,1,2));
return 0;
}
1) 说明:_Generic用于类型安全。将其转换为字符类型,根据int型一维数组x的大小和int型y的大小进行按字节指针算术运算。优先级为[]大于&。&是为了获取地址,然后将其转换为int*并取消引用。该宏将返回该值。
a[0][6]
等同于a[1][2]
,但已经确定,根据标准,尝试访问a[0][6]
确实是未定义的。它可能看起来可以工作(正如它确实对您起作用),但不能保证其可行性。 - Steve Summitunion
成员来获取通过1D数组和2D数组(或不同的2D数组)进行访问。 - chux - Reinstate Monica