严格的别名规则,误报还是漏报?

Lig*_*Bit 2 c strict-aliasing gcc-warning

我在 C 中遇到严格别名问题。我使用的是 GCC 4.7.1。

示例 1:
当使用 -fstrict-aliasing -Wstrict-aliasing=3 编译此代码时,我收到“警告:取消引用类型双关指针将破坏严格别名规则”

#include <stdio.h>
#include <stdint.h>

int main(void)
{
    uint8_t a[4] = {0x01, 0x23, 0x45, 0x67};
    uint32_t b;

    b = *(uint32_t *)a;

    printf("%x\n", b);

    return(0);
}
Run Code Online (Sandbox Code Playgroud)


示例 2:
此代码使用 -fstrict-aliasing 和 -Wstrict-aliasing=3 或 -Wstrict-aliasing=2 或 -Wstrict-aliasing=1 时不发出警告

#include <stdio.h>
#include <stdint.h>

int main(void)
{
    uint8_t a[4] = {0x01, 0x23, 0x45, 0x67};
    uint32_t b;
    void *p;

    p = a;
    b = *(uint32_t *)p;

    printf("%x\n", b);

    return(0);
}
Run Code Online (Sandbox Code Playgroud)


两个例子都工作正常。

使用 union 也是未定义的行为,并且在我的情况下使用 memcpy() 太慢。
那么,第一个示例是安全的(误报)还是第二个示例也不安全(误报)还是......?

谢谢。

Who*_*aig 5

uint32_t如果您想从 4制造 a uint8_t,那么就这样做:制造它。不要尝试从指针转换以外的东西中取出一个。您提供的代码的结果会有所不同,具体取决于您的平台是小端还是大端,更不用说它是完全错误的。

他们俩都很糟糕。无论如何,提供的两个样本都是不安全的。此类强制转换可以绕过数据对齐要求。如果您要转换为“目标”的任何内容可能比您要转换为“来源”的任何内容需要更严格的对齐,那么您就会引发总线错误。请注意最初的警告。中间指向 void 的指针只是掩盖了问题(就像大多数问题一样)。

当您构建该字节时,您想知道哪个字节要去“哪里” uin32_t。

uint8_t a[4] = {0x01, 0x23, 0x45, 0x67};
uint32_t b = ((uint32_t)a[0] << 24) |
             ((uint32_t)a[1] << 16) |
             ((uint32_t)a[2] << 8) |
             (uint32_t)a[3];
Run Code Online (Sandbox Code Playgroud)

这将始终将 a[0] 字节放在目标 32 位无符号的高字节中,将 a[1] 放在下一个字节中,等等,无论字节序如何。一直b会。​0x01234567

  • @JensGustedt,我同意这个“参考”,但演员晋升不是通过参考完成的;它是按价值完成的。然而,我总是愿意看到不同的一面,所以如果你能举一个具体的例子,其中像上面这样的价值提升可能(甚至更好,*确实*)导致对齐问题,我非常感兴趣。同样,如果上述内容明确违反了标准(因此也违反了 UB),请注明它违反了标准的哪一部分。我已经介绍了 C99 6.3,并且不知道这违反了任何内容,因此如果还有其他内容,请分享。 (2认同)