C 严格的别名规则,假阳性还是假阴性?

C 严格的别名规则,假阳性还是假阴性?,c,gcc-warning,strict-aliasing,C,Gcc Warning,Strict Aliasing,我在C语言中有一个严格的别名问题。我使用的是GCC 4.7.1。 示例1: 当使用-fstrict aliasing-Wstrict aliasing=3编译此代码时,我得到“警告:取消引用类型punned指针将破坏严格的别名规则” #包括 #包括 内部主(空) { uint8_t a[4]={0x01,0x23,0x45,0x67}; uint32_t b; b=*(uint32_t*)a; printf(“%x\n”,b); 返回(0); } 示例2: 当-fstrict aliasin

我在C语言中有一个严格的别名问题。我使用的是GCC 4.7.1。

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

#包括
#包括
内部主(空)
{
uint8_t a[4]={0x01,0x23,0x45,0x67};
uint32_t b;
b=*(uint32_t*)a;
printf(“%x\n”,b);
返回(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);
}
#包括
#包括
内部主(空)
{
uint8_t a[4]={0x01,0x23,0x45,0x67};
uint32_t b;
void*p;
p=a;
b=*(uint32_t*)p;
printf(“%x\n”,b);
返回(0);
}

两个示例都能正确运行。

使用union也是未定义的行为,在我的例子中,使用memcpy()太慢了。
那么,第一个例子是安全的(假阳性),还是第二个例子也是不安全的(假阴性),或者……?


谢谢。

我想说第二个例子也是不安全的-只是在这种情况下,编译器没有足够的智能来发现
p
a
实际上指向相同的(1字节对齐)位置,而且因为
void*
不能对齐(根据定义-什么是
sizeof(void)
?),它不会发出警告。

如果您想从4
uint8\u t
制造
uint32\u t
,则只需执行以下操作:制造它。不要试图从不是一个指针投射的东西中抽取一个。根据您所使用的平台是little endian还是big endian,您提供的代码的结果会有所不同,更不用说它完全是错误的了

他们都很坏。无论怎样,提供的两个样品都是不安全的。这种强制转换绕过了数据对齐要求。如果您要“向”强制转换的任何对象比“从”强制转换的任何对象都可能需要更严格的对齐,则会导致总线错误。注意最初的警告。指向void的中间指针只是掩盖了问题(对于大多数问题也是如此)

您想知道当您构建该
uin32\u 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];
uint8_t a[4]={0x01、0x23、0x45、0x67};

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


C表示必须通过对象本身的类型、有符号的变体或字符类型访问对象,否则会违反别名规则。

只有在endian为“正确”时,我才会使用它,但我担心严格的别名。不,这不是制造
uint32_t
的方法,这是由于对齐问题而未定义的行为。强制转换到可能具有更严格对齐的类型从来都不是一个好主意。制造
uint32_t
的方法是另一种方法,创建
uint32_t
并通过
unsigned char
@JensGustedt访问其字节,我同意这里的引用方式,但cast提升不是通过引用实现的,而是通过值实现的。然而,我总是愿意看到不同的一面,因此如果你能举一个具体的例子,上面提到的值提升可以(甚至更好,可以实现)引起一个对齐问题我很感兴趣。同样,如果上面有什么东西特别违反了标准(因此UB)请注意它违反了标准的哪一小节。我已经介绍了C99 6.3,并且不知道这违反了什么,所以如果还有其他内容,请分享。@JensGustedt聊天中的人说我在第一句话中的措辞是错误的,也许你认为我同意通过不安全的别名为ge铸造的OP他要找的是uint32\t。我不是说他的代码是正确的。相反,如果这一半有混淆,我会稍微修改开头的句子,以使评估更清楚。也就是说,如果有UB问题,我肯定有兴趣知道答案。@WhozCraig,是的,现在不那么模棱两可了,很好。我仍然不认为你回答了这个问题(但OP确实这么认为:)。有一些方法可以获得OP想要实现的目标(将内存中的字节顺序视为整数)。只有他在问题中发布的内容也不是正确答案。C99 TC3脚注82明确允许使用联合进行类型双关。在这里,别名不是您的问题,对齐是。无法保证对齐
uint8_t
aka
无符号字符的数组,以便您可以通过指向
u的指针读取它int32\t
。别那样做,你会惹麻烦的。(反过来也行。)不,我认为强制转换到
void*
会告诉编译器不要对该指针有任何期望,因此从别名的角度来看,这是可以的。更大的问题是对齐。这就是行为未定义的原因。@JensGustedt是的,我没有告诉任何其他内容(至少我希望我的意思很清楚).对我来说不是那么清楚。事实上,编译器必须足够聪明,能够看到他不能对
*p
@JensGustedt进行任何假设是的,这就是我所说的“void*不能对齐”。
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];