2

我在 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);
}


示例 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);
}


这两个示例都可以正常工作。

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

谢谢。

4

3 回答 3

5

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

他们俩都很坏。无论如何,提供的两个样本都不安全。此类强制转换绕过了数据对齐要求。如果您正在投射“to”的任何东西都需要比您正在投射“from”的任何东西更具限制性的对齐方式,那么您就会引发总线错误。注意最初的警告。指向无效的中间指针只是掩盖了问题(就像它对大多数问题所做的那样)。

您想知道构建时哪个字节“在哪里” 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];

这将始终将 a[0] 字节放在目标 32 位无符号的高字节中,将 a[1] 放在下一个字节中,依此类推,无论字节顺序如何。b永远都是0x01234567。_

于 2012-12-04T17:41:18.350 回答
2

我想说第二个例子也是不安全的 - 只是编译器不够聪明,无法发现它pa实际上指向相同的(1字节对齐)位置,并且因为void *无法对齐(根据定义- 会是sizeof(void)什么?),它不会发出警告。

于 2012-12-04T17:37:56.197 回答
1

在这两种情况下,您都uint8_t通过另一种类型 ( uint32_t) 访问数组元素 (type ),该类型不是原始类型的有符号变体,也不是字符类型。

C 表示您必须通过对象自己的类型或签名变体或字符类型来访问对象,否则您将违反别名规则。

于 2012-12-04T17:58:02.350 回答