【发布时间】:2014-09-26 20:54:43
【问题描述】:
我最近遇到了一个讨厌的 schrödinbug。在尝试将文件加载到平面内存表示中时,作者编写了如下代码:
class Line final { public:
int stuff[3];
char* data;
}
//...
Line* line = /*...*/;
//Trying to treat line->data like an array. This is *wrong*.
line->data = reinterpret_cast<char*>(line) + 3*sizeof(int);
//...
line->data[0] = /*...*/
line->data[1] = /*...*/
//...
line->data[n] = /*...*/ //"line->data" changes because of this line!
所以,发生的事情是第一行代码基本上将line->data 设置为等于&line->data。这是一个错误,因为对line->data 指向的值的任何更改也可能会更改line->data 本身指向的内容!
我当时很奇怪,问题发生的时间太长了。我的理解是,除非使用 restrict(或 g++/MSVC __restrict)限定,否则编译器必须假定指针是别名的。因此,如果我将line->data[0] 设置为某个东西,那么它将对下一次访问line->data[1] 可见,并且几乎肯定会无效。然而,在调试器中,更改直到很久以后才可见,并且写入愉快地继续了一段时间。
我猜编译器(在本例中为 MSVC 2013)不认为自别名是可能的。允许吗?
【问题讨论】:
-
你的问题基本上是这样的:“为什么
line->data[1] = ...没有崩溃?”? -
是的。在计算
line->data[1]时,line->data[0]的写入应该对line->data的后续负载可见——除非别名不包括自锯齿。 -
可能是因为填充,取决于系统的位数。
-
嗯,马上想到的一件事就是对齐。这是在 64 位平台上运行的吗?如果是这样,则指针算术中的计算没有考虑填充。
-
@OliverCharlesworth @T.C.这确实是在 64 位平台上。你能澄清一下填充是如何应用的吗?
sizeof(Line)==24,所以我猜在data之前有一个4字节填充——这使得line->data稍微在&line->data之前。有趣的是,虽然变化经常发生在4附近,但有时可能会更高。
标签: c++ restrict restrict-qualifier