这段C++代码是否可移植?(假设多维数组具有连续的内存布局)

19

首先,如果我有任何语法错误等,请原谅我的英文...

我的问题是,当我们有一个二维数组时,如果我没记错的话,从计算机和 C/C++ 的角度来看,它只是一个长的一维数组,索引只是帮助编译器映射到具体的地址。

这段代码片段在 Visual C++ 中可以工作,但我想知道,这段代码是否 可移植符合 标准(C++98),在其他架构和/或操作系统上不会出现意外:

int arr[][3] = { 1, 5, 3, 7, 5, 2, 7, 8, 9 };
const int ARR_NUM = sizeof(arr) / sizeof(int);

int* ptr = reinterpret_cast<int*>(arr);    // NOT: int(*)[][3] !!!
for (int i = 0; i < ARR_NUM; ++i) {
    cout << ptr[i] << endl;
}

4
我更改了标签,因为[multidimensional]的标签维基建议使用[multidimensional-array]代替。我建议将[arrays]替换为[language-lawyer],这样可能会吸引到合适的人。 - Matt K
2
我会使用int* p = &arr[0][0],而不是reinterpret_cast。 - user396672
1
@user396672,int * p = arr[0]; 等同于 int * p = &arr[0][0];,但更加清晰易懂。 - Griwes
@Griwes:通常,简洁并不等于易读。例如,我总是更喜欢使用&arr[i]而不是array+i,因为表达式&arr[i]清楚地说明了arr是一个数组,我不需要去查看arr的定义来理解这里发生了什么(除了C和C++中指针算术是最有争议的特性之一)。我完全理解你的观点,但对我来说int * p = arr[0]不够清晰:要理解它,我需要几秒钟的时间;而要理解&arr[0][0],我只需要几百毫秒 :) - user396672
1
@user396672,那你有点奇怪(无意冒犯):P - Griwes
显示剩余2条评论
4个回答

15

标准语

多维数组的元素按行优先顺序连续存储,因此手动索引是可移植的:

C++98,8.3.4/1:

数组类型对象包含一组类型为 T 的 N 个连续分配的非空子对象。

显然,对于多维数组,这递归地适用。

然而,这种使用 reinterpret_cast 并不可移植。标准规定(C++98,5.2.10/1):

[...]否则,结果是右值,[...],数组到指针,[...]标准转换应用于表达式 v。

换句话说,传递 arr 立即触发将数组衰减为指向其第一个元素的指针。然后(C++98,5.2.10/3)出现了万能的

reinterpret_cast 执行的映射是实现定义的。

本节的其余部分列出了许多例外情况,指定了始终定义良好的强制转换。由于没有一个适用于这里,结论是从技术上讲默认情况下它是实现定义的。

最终结论

理论上讲,这是不可移植的。实际上,只要架构相同(例如 x86),我肯定希望强制转换能够可靠地工作。

幸运的是,您不必假设任何类似的事情,因为正如其他人所提到的,像 int* ptr = arr[0] 这样的东西可以完成同样的工作,并且保证可移植性。


不确定这是正确的引用:它提到T1T2是对象,我认为这与指针具体不同。 - Matthieu M.
根据Bjarne Stroustrup的书,reinterpret_cast是实现定义的,因为它通过创建新的目标指针类型,并将位模式从源类型复制到其中来工作。为什么它是实现定义的呢?因为这可能会导致对齐变化,在某些系统上,不同指针类型之间的字节数可能不同。这并不意味着(在我看来)reinterpret_cast不可移植。在这种情况下,此转换是实现定义的,但在语义上有意义,并且应该在不同系统中产生相同的行为。 - Benj
8.3.4节是正确的章节,它严格定义了数组的内存布局,使得相关代码具有可移植性。此外,请注意,“_object_”指代任何内存条目,因此适用于所有类型(不仅限于类类型)。 - edA-qa mort-ora-y
@Benj:你不仅在问可移植性的问题,还在问它是否符合标准。 - Niklas B.
@bames53:不,不能有填充。 “连续”表示首先在int之间没有填充,然后递归地表示在int[]之间没有填充等等。 - Jon
显示剩余6条评论

4
如果您想非常严格,则reinterpret_cast在标准中的定义并不十分明确。这段代码可以在任何地方运行,但您可能会对其提出一些吹毛求疵的反驳。
使用:
int *ptr = arr[0];

为了绝对保险起见,连续的数组布局是有保证的。

0

关于多维数组具有连续内存布局的假设,它符合标准且可移植。自C时代以来一直如此,C++也没有改变这一点。

然而,reinterpret_cast不是可移植的,因此你的代码不能保证在任何地方都能正常工作。


首先,这是关于C语言而不是C++。第二,该段落讨论了数组的下标访问。在这种情况下,reinterpret_cast应该是具体实现定义的,但我不确定。 - Niklas B.

0

如果我没记错,reinterpret_cast 不是一个可移植的操作。


2
这取决于情况。有些reinterpret_cast的用法是完全规范的。 - Matthieu M.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接