【问题标题】:Bad value affectation after type casting类型转换后的不良价值影响
【发布时间】:2011-12-29 22:34:39
【问题描述】:

我使用本地无符号长变量作为缓冲区,用于在其中包含两个无符号短变量。根据我对 C++ 的了解,它应该是一种有效的方法。我多次使用这种方法将 2 个 unsigned char 存储在一个 unsigned short 中,没有任何问题。不幸的是,当在不同的架构上使用它时,它的反应很奇怪。似乎在第二次分配后更新了值。 (溢出)案例只是为了证明它。谁能解释一下它为什么会这样反应?

unsigned long dwTest = 0xFFEEDDCC;

printf("sizeof(unsigned short) = %d\n", sizeof(unsigned short));
printf("dwTest = %08X\n", dwTest);

//Address + values
printf("Addresses + Values: %08X <- %08X, %08X <- %08X\n", (DWORD)(&((unsigned short*)&dwTest)[0]), (((unsigned short*)&dwTest)[0]), (DWORD)(&((unsigned short*)&dwTest)[1]), (((unsigned short*)&dwTest)[1]) );

((unsigned short*)&dwTest)[0] = (WORD)0xAAAA;
printf("dwTest = %08X\n", dwTest);

((unsigned short*)&dwTest)[1] = (WORD)0xBBBB;
printf("dwTest = %08X\n", dwTest);

//(Overflow)
((unsigned short*)&dwTest)[2] = (WORD)0x9999;

printf("dwTest = %08X\n", dwTest);

Visual C++ 2010 输出(正常):

sizeof(unsigned short) = 2
dwTest = FFEEDDCC
Addresses + Values: 0031F728 <- 0000DDCC, 0031F72A <- 0000FFEE

dwTest = FFEEAAAA

dwTest = BBBBAAAA

dwTest = BBBBAAAA

ARM9 GCC Crosstool 输出(不起作用):

sizeof(unsigned short) = 2
dwTest = FFEEDDCC
Addresses + Values: 7FAFECD8 <- 0000DDCC, 7FAFECDA <- 0000FFEE

dwTest = FFEEDDCC

dwTest = FFEEAAAA

dwTest = BBBBAAAA

【问题讨论】:

  • 编译时启用警告 (-Wall) 并查看警告。然后在 SO 上查看“严格别名”(解决方案:使用联合)。

标签: casting arm unsigned


【解决方案1】:

您正在尝试做的事情称为类型双关语。有两种传统方法可以做到这一点。

一种方法是通过指针(您所做的)。不幸的是,这与优化器冲突。您会看到,由于停止问题,优化器在一般情况下无法知道两个指针不会相互别名。这意味着编译器必须重新加载任何可能已通过指针修改的值,从而导致大量可能不必要的重新加载。

因此,引入了严格别名规则。它基本上说两个指针只有在它们属于同一类型时才能相互别名。作为一个特殊规则,char * 可以为任何其他指针起别名(但不能反过来)。 这打破了通过指针的类型双关,让编译器生成更高效的代码。当 gcc 检测到类型双关并启用警告时,它会警告您:

warning: dereferencing type-punned pointer will break strict-aliasing rules

另一种进行类型双关的方法是通过联合:

union {
    int i;
    short s[2];
} u;
u.i = 0xDEADBEEF;
u.s[0] = 0xBABE;
....

这打开了一整罐新的蠕虫。在最好的情况下,这取决于实现。现在,我无法访问 C89 标准,但在 C99 中,它最初声明除了最后一个存储到的联合成员之外的联合成员的值是未指定的。这在 TC 中进行了更改,以声明未指定与最后存储到成员不对应的字节值,并声明与最后存储到成员相对应的字节将根据新的重新解释类型(显然依赖于实现的东西)。

对于 C++,我在标准中找不到关于 union hack 的语言。无论如何,C++ 有 reinterpret_cast&lt;&gt;,这是你应该在 C++ 中用于类型双关的(使用 reinterpret_cast&lt;&gt; 的参考变体)。

无论如何,您可能不应该使用类型双关语(依赖于实现),您应该通过位移手动建立您的值。

【讨论】:

  • 感谢您提供完整的澄清答案
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-01-17
  • 2021-02-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-16
相关资源
最近更新 更多