为什么在C/C++中,需要一个接收MD数组的函数参数具有所有子数组/维度大小的大小?
这里(PDF):它说MD数组的唯一区别是“编译器记住每个虚拟维度”,但当我违反这些维度时,编译器什么也不做,例如:
char arr[3][5];
arr[0][5] = 10;
那么,记住这些尺寸的意义是什么呢?
数组的索引访问必须基于索引值和声明的次优维度按行主序计算内存偏移量。稍后会详细解释。
但首先,您的问题与以下简单观察密切相关:
void foo( char a[] )
{
a[5] = 'a';
}
// caller of foo() from somewhere
char arr[5];
foo(arr);
为什么编译器会让你做那种事情?因为这是C语言,而且您完全有权利使用未定义行为来扯下自己的一只脚。请记住:
void foo( char a[][5] )
{
a[0][5] = 'a';
}
// caller of foo() from somewhere
char arr[4][5];
foo(arr);
这段代码与之前的代码同样“有效” (也就是说,你有权冒着风险和危险进入UB)。在这种情况下,它会“工作”,但只因为底层数组的线性背景宽度为20个元素,并且我们只访问第六个元素,也就是技术上的arr[1][0]
。
这些较低的维度的目的在于正确地计算类似于这样的访问:
void foo( char a[][5] )
{
a[2][1] = 'b';
}
2
优先指数必须使用已声明的较低维度(在本例中为5
)来有效地计算正确元素的线性偏移量。将2D数组布局在1D线性块中,用于执行此操作:
char arr[20]; // 4*5
arr[2*5+1] = 'b';
5
。它是声明的低维度,必须知道才能正确计算行跳跃(比喻)。char arr[3][4][5];
arr[1][2][3] = 'c';
有效地计算出线性背景下基础数组的正确位置:
char arr[60]; // 3*4*5
arr[ 1*(4*5) + 2*(5) + 3 ] = 'c';
等等。将其扩展到您想要的任意维度。为了正确执行此操作,必须知道所有较低的维度。
数组不是一种特殊的对象,它只是一个项目的长列表。你的 arr[3][5]
实际上只是一个 arr[15]
,编译器通过重定向将 arr[0][5]
重定向到 arr[5]
。
因为 C/C++ 不存储大小,所以您需要硬编码它们,以使 [0][5]
正确映射到 [5]
。
某些编译器可能会强制执行错误的 [0][5]
(或警告它),但由于它映射到 [5]
,至少会做些什么。