当问到C语言中常见的未定义行为时,人们有时会提到严格的混叠规则。 他们在谈论什么?


当前回答

严格的混叠是不允许不同的指针类型指向相同的数据。

本文将帮助您全面详细地理解这个问题。

其他回答

通过指针强制转换(而不是使用联合)的类型双关是打破严格混叠的一个主要例子。

作为Doug T.已经写过的附录,在这里 是一个简单的测试用例,可能会触发GCC:

check.c

#include <stdio.h>

void check(short *h,long *k)
{
    *h=5;
    *k=6;
    if (*h == 5)
        printf("strict aliasing problem\n");
}

int main(void)
{
    long      k[1];
    check((short *)k,k);
    return 0;
}

编译gcc -O2 -o check check.c。 通常(我尝试过的大多数gcc版本)这会输出“严格的混叠问题”,因为编译器假设“h”不能与“check”函数中的“k”地址相同。因此,编译器会优化if (*h == 5),并始终调用printf。

对于那些感兴趣的人,这里有x64汇编代码,由gcc 4.6.3生成,运行在ubuntu 12.04.2的x64上:

movw    $5, (%rdi)
movq    $6, (%rsi)
movl    $.LC0, %edi
jmp puts

所以if条件从汇编代码中完全消失了。

严格的混叠是不允许不同的指针类型指向相同的数据。

本文将帮助您全面详细地理解这个问题。

我找到的最好的解释是Mike Acton的《Understanding Strict Aliasing》。本文主要关注PS3的开发,但这基本上只是GCC的工作。

摘自文章:

严格混叠是C(或c++)编译器的一个假设,即指向不同类型对象的指针的解引用永远不会指向相同的内存位置(即相互混叠)。

所以基本上,如果你有一个int*指向一些包含int型的内存,然后你把一个float*指向那个内存,并把它用作浮点数,你就违反了规则。如果你的代码不尊重这一点,那么编译器的优化器很可能会破坏你的代码。

该规则的例外是一个char*,它被允许指向任何类型。

从技术上讲,在c++中,严格的混叠规则可能永远都不适用。

注意indirection(*运算符)的定义:

一元*运算符执行间接操作:它所对应的表达式 是指向对象类型的指针,还是指向对象类型的指针 函数类型,结果是指向对象或的左值 表达式所指向的函数。

同样来自glvalue的定义

glvalue是一个表达式,其求值决定的标识 一个对象,(…剪)

因此,在任何定义良好的程序跟踪中,glvalue都指向对象。所以所谓的严格混叠规则并不适用。这可能不是设计师想要的。