为什么在 C/C++ 中,接收 MD arr 的 func 参数需要具有所有子数组/维度的大小?
这里(PDF):它说 MD arrs 的唯一区别是“编译器记住每个虚构的维度”但是当我违反这些维度时,编译器什么也不做,例如:
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
)来有效地计算适当元素的线性偏移。在 1D 线性块中布置 2D 数组,它用于执行以下操作:
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]
,它至少会做一些事情。