2014-09-02 88 views
2

我最近 相结合的三元运营位域访问时遇到一个棘手/有趣的问题,该代码已被简化足以暴露只是问题。位域三元操作作为左值

#include "stdafx.h" 
#include <stdint.h> 

typedef union 
{ 
    struct 
    { 
     uint32_t Bit_0_1  : 2; 
     uint32_t Bit_2  : 1; 
     uint32_t Bit_3_To_31 : 29; 
    } BitField; 
    uint32_t RawValue; 
} UnnamedUnion; 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    UnnamedUnion a; 
    UnnamedUnion b; 
    a.RawValue = 0; 
    b.RawValue = 0; 
    //Version 1. Works 
    if (1 == 1) 
    { 
     a.BitField.Bit_0_1 = 0; 
    } 
    else 
    { 
     b.BitField.Bit_0_1 = 0; 
    } 
    printf("Reg value : %u\n", a.RawValue); 
    //Version 2. Also works 
    (1 == 1 ? a.RawValue : b.RawValue) = 1; 
    printf("Reg value : %u\n", a.RawValue); 
    //Version 3. Crashes! 
    (1 == 1 ? a.BitField.Bit_0_1 : b.BitField.Bit_0_1) = 2; 
    printf("Reg value : %u\n", a.RawValue); 

    getchar(); 
    return 0; 
} 

请注意,版本1,2,3都是等效的表达式。我对这个 是如何发生的我有我的假设,因为我从来没有编写过编译器,但是想听听大家的想法!
更新:确认失败既VS 2012 更新:代码被再次编辑,真正把精力放在问题孤单。 (没有了铸造)

+0

我想你正在使用VC,究竟是什么版本?我用你的代码尝试了gcc和clang,没有崩溃,打印0,1,2。 – 2014-09-02 22:31:36

+0

看起来像(特别是你的第二次更新消除了可能的别名红鲱鱼),你已经发现了MSVC中的一个codegen错误。您应该在connect.microsoft.com上打开一个错误报告。 – 2014-09-03 01:34:00

+0

感谢您的精彩见解。票证已提交:https://connect.microsoft.com/VisualStudio/feedbackdetail/view/962553/codegen-bug-in-accessing-bitfield-in-ternary-operations-as-lvalue – user3788697 2014-09-03 05:10:25

回答

0

试试这个:

reinterpret_cast<UnnamedUnion&>(someArray[(1==1)?0:1]).RawValue = 1; 

针对指定的C++布尔性质,甚至可以简单地简化为:

reinterpret_cast<UnnamedUnion&>(someArray[(1!=1)]).RawValue = 1; 
+1

这不是一个答案,它只会提供更多的信息。它应该是一个评论。 – 2014-09-02 23:48:10

1

版本1和3肯定违反严格别名规则,这是未定义的行为。鉴于此,版本1在版本3失败时工作肯定是一种可能未定义的行为。

相信版本2也不确定的,但我也不太记得,如果工会允许别名的积极成员的类型,所以它可能是非常明确的(无论哪种方式,你的代码的工作是有效的结果)。

鉴于此,试图猜测为什么一个编译器,一组编译器选项和一组标准库在一个特定硬件上可能会选择一组特定的未定义的行为来执行。

+1

实际上它与严格的别名规则无关,请尝试下面的代码:UnnamedUnion obj; (1 == 1?obj.BitField.Bit_0_1:obj.BitField.Bit_0_1)= 2;它仍然没有相同的签名。 – user3788697 2014-09-03 01:10:11