2013-08-20 95 views
1

一个工会,我想知道,如果把指针转换为uint32_t的包含一个uint32_t的会导致C中定义的行为联合的指针,即C来自指针投射到uint32_t的一个指向包含uint32_t的

typedef union 
{ 
    uint8_t u8[4]; 
    uint32_t u32; 
} T32; 

void change_value(T32 *t32) 
{ 
    t32->u32 = 5678; 
} 

int main() 
{ 
    uint32_t value = 1234; 

    change_value((T32 *)&value); // value is 5678 afterwards 

    return EXIT_SUCCESS; 
} 

这是有效的C吗?提前谢谢了。

+1

你至少试过了吗? – ThibThib

+7

@ThibThib UB很难通过“尝试”来检测。 – 2013-08-20 20:04:08

+1

我试过了,正如评论所述,它完美地工作。但是,我不确定,因为投射指针有时会有些棘手。 – CastingCrows

回答

3

您的问题的一般答案是,不,这通常没有定义。如果联合包含比uint32_t更大的对齐字段,则这样的联合必须具有最大对齐并访问该指针然后才能导致UB。例如,如果您将示例中的uint8_t替换为double,则可能会发生这种情况。

然而,在您的具体情况下,行为已被很好地定义。 uint8_t,如果存在的话,最有可能只是unsigned char而且所有字符类型的对齐要求总是最少的。

编辑: 正如R.在他的评论中提到的那样,您的方法还存在其他问题。首先,理论上,如果存在该宽度的无符号“扩展整数类型”,则uint8_t可能不同于unsigned char。这是不太可能的,我从来没有听说过这样的架构。其次,你的方法受到锯齿问题的影响,所以你应该非常小心。

+0

我认为还有一个别名问题。当所讨论的对象具有联合类型时,编译器必须小心处理别名假设,而事实并非如此。联合类型的存在,其中一个成员匹配另一个对象的类型,不允许别名该对象。 –

+0

请注意,如果'uint8_t'被定义为'unsigned char',则别名问题不成问题。但是,'uint8_t'可以被定义为一个与'unsigned char'具有相同范围的扩展整数类型。它甚至可以有不同的表示(不同的位顺序)。 –

0

由于所有联合成员都保证从相同的内存地址开始,因此您编写的程序不会导致未定义的行为。

2

冒着降低风险的风险......从概念上讲,你所要做的事情没有任何问题。也就是说,定义一个可以看作是四个字节和一个32位整数的存储器,然后使用指针引用和修改该存储器。

但是,我会问你为什么要写代码的目的是模糊的。你真正在做的是迫使下一位读代码的程序员思考几分钟,甚至尝试一下测试程序。因此,这种编程风格是“昂贵的”。

你本来可以很容易地定义,值:

T32 value; 
// etc. 
change_value(&value); 

,然后避开演员和随后的焦虑。

+0

你已经招致了我的倒下。开玩笑。 +1 – Jiminion

+0

我的意图是change_value可以用于uint32_t以及T32。但是,考虑到别名问题,它可能归结为上面示例中显示的代码。 – CastingCrows