当询问 common undefined behavior in C ,人们有时会提到严格的别名规则。
他们在说什么?
最佳答案
遇到严格别名问题的典型情况是将结构(如设备/网络消息)覆盖到系统字大小的缓冲区(如指向 uint32_t
s 或 uint16_t
s 的指针)。当您将结构覆盖到这样的缓冲区上,或者通过指针转换将缓冲区覆盖到这样的结构上时,您很容易违反严格的别名规则。
所以在这种设置中,如果我想向某些东西发送消息,我必须有两个不兼容的指针指向同一块内存。然后我可能会天真地编写这样的代码:
typedef struct Msg
{
unsigned int a;
unsigned int b;
} Msg;
void SendWord(uint32_t);
int main(void)
{
// Get a 32-bit buffer from the system
uint32_t* buff = malloc(sizeof(Msg));
// Alias that buffer through message
Msg* msg = (Msg*)(buff);
// Send a bunch of messages
for (int i = 0; i < 10; ++i)
{
msg->a = i;
msg->b = i+1;
SendWord(buff[0]);
SendWord(buff[1]);
}
}
严格的别名规则使此设置非法:取消引用为不属于 compatible type 的对象设置别名的指针。或 C 2011 6.5 第 71 段允许的其他类型之一是未定义行为。不幸的是,你仍然可以用这种方式编码,也许会得到一些警告,让它编译正常,只是在你运行代码时出现奇怪的意外行为。(GCC 在给出别名警告的能力上似乎有些不一致,有时会给我们一个友好的警告,有时则不会。)
要了解为什么这种行为是未定义的,我们必须考虑严格的别名规则给编译器带来了什么。基本上,有了这个规则,就不用考虑插入指令来刷新
buff
的内容了。每次循环运行。相反,在优化时,使用一些令人讨厌的关于别名的非强制假设,它可以省略这些指令,加载 buff[0]
和 buff[1]
在循环运行之前进入 CPU 寄存器一次,并加速循环体。在引入严格别名之前,编译器不得不对 buff
的内容感到 panic 。可以通过任何先前的内存存储进行更改。因此,为了获得额外的性能优势,并假设大多数人不键入双关指针,则引入了严格的别名规则。请记住,如果您认为该示例是人为设计的,那么如果您将缓冲区传递给另一个为您进行发送的函数(如果您有),甚至可能会发生这种情况。
void SendMessage(uint32_t* buff, size_t size32)
{
for (int i = 0; i < size32; ++i)
{
SendWord(buff[i]);
}
}
并重写我们之前的循环以利用这个方便的功能for (int i = 0; i < 10; ++i)
{
msg->a = i;
msg->b = i+1;
SendMessage(buff, 2);
}
编译器可能会也可能不会或足够聪明来尝试内联 SendMessage 并且它可能会也可能不会决定再次加载或不加载 buff。如 SendMessage
是另一个单独编译的 API 的一部分,它可能有加载 buff 内容的指令。再说一次,也许您使用的是 C++,这是编译器认为可以内联的一些模板化头文件实现。或者,这可能只是您为方便起见而在 .c 文件中编写的内容。无论如何,未定义的行为可能仍然会发生。即使我们知道一些幕后发生的事情,它仍然违反了规则,因此不能保证明确定义的行为。因此,仅仅通过包装在我们的单词分隔缓冲区的函数中不一定有帮助。那么我该如何解决这个问题呢?
union {
Msg msg;
unsigned int asBuffer[sizeof(Msg)/sizeof(unsigned int)];
};
char*
用于别名而不是系统的单词。规则允许 char*
的异常(exception)情况(包括 signed char
和 unsigned char
)。总是假设 char*
别名其他类型。然而,这不会以其他方式工作:没有假设您的结构为字符缓冲区别名。初学者当心
当两种类型相互叠加时,这只是一个潜在的雷区。您还应该了解 endianness , word alignment ,以及如何通过 packing structs 处理对齐问题正确。
脚注
1 C 2011 6.5 7 允许左值访问的类型是:
关于c++ - 什么是严格的别名规则?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/98650/