再一次:严格的别名规则和char *

时间:2015-01-30 15:59:03

标签: c++ c reinterpret-cast strict-aliasing

我读的越多,我就越困惑。

相关问题的最后一个问题与我的问题最接近,但我对所有关于对象生命周期的问题感到困惑,尤其是 - 只读或不读。


直截了当。如果我错了,请纠正我。

这没关系,gcc没有发出警告,我正试图通过T“读取uint32_tchar*)类型:”

uint32_t num = 0x01020304;
char* buff = reinterpret_cast< char* >( &num );

但这是“坏”(也是警告)而我正在尝试“反过来”:

char buff[ 4 ] = { 0x1, 0x2, 0x3, 0x4 };
uint32_t num = *reinterpret_cast< uint32_t* >( buff );

第二个与第一个有什么不同,特别是当我们谈论重新排序指令(用于优化)时?另外,添加const并不会以任何方式改变这种情况。

或者这只是一条直接的规则,它明确指出:“这可以在一个方向完成,但在另一个方向不能完成”? 我在标准中找不到任何相关内容(特别是在C ++ 11标准中搜索过这个内容)。

C和C ++是否相同(因为我读了一条评论,暗示这两种语言有所不同)?


我使用union来“解决”这个问题,但仍然显示 100%正常,因为标准无法保证(这表明我只能依赖值,在union)中最后修改。

所以,在阅读很多之后,我现在更加困惑了。我想只有memcpy是“好”的解决方案?


相关问题:


修改
现实世界的情况:我有第三方lib(http://www.fastcrypto.org/),它计算UMAC,返回值在char[ 4 ]。然后我需要将其转换为uint32_t。而且,顺便说一下,lib会使用像((UINT32 *)pc->nonce)[0] = ((UINT32 *)nonce)[0]这样的东西。反正。

另外,我问的是什么是对的,什么是错的以及为什么。不仅仅是关于重新排序,优化等等(有趣的是-O0没有警告,只有-O2)。

请注意:我知道大/小端情况。情况并非如此。我真的想忽略这里的字节序。 “严格的别名规则”听起来像是非常严肃的事情,远比错误的字节序严重得多。我的意思是 - 就像访问/修改内存一样,不应该被触及; 任何种类的UB。

标准(C和C ++)引用将非常感激。我找不到任何关于别名规则或任何相关规则的内容。

2 个答案:

答案 0 :(得分:8)

  

第二个与第一个有什么不同,特别是当我们谈论重新排序指令(用于优化)时?

问题在于编译器使用规则来确定是否允许这样的优化。在第二种情况下,您尝试通过不兼容的指针类型读取char[]对象,这是未定义的行为;因此,编译器可能会重新排序读取和写入(或执行您可能不期望的任何其他操作)。

尽管看起来不自然,但你真的不得不考虑你认为编译器可能如何优化,而只是遵守规则

  

或者这只是一条直接的规则,它明确指出:“这可以在一个方向完成,但在另一个方向不能完成”?我在标准中找不到任何相关内容(特别是在C ++ 11标准中搜索过这个内容)。

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf第3.10章第10段。

在C99中,我认为也是C11,它是6.5段第7段。

C和C ++都允许通过char *(或者特别是类型为char的左值)访问任何对象类型。它们不允许通过任意类型访问char对象。所以是的,规则是一种“单向”规则。

  

我使用了union来解决这个问题,这似乎仍然不是100%正常,因为标准不能保证(这表明我只能依赖于联合中最后修改的值)

尽管该标准的措辞非常模糊,但在C99(以及更高版本)中,很明显(至少从C99 TC3开始)意图是允许通过联合进行类型惩罚。但是,您必须通过联合执行所有访问(特别是您不能仅仅为了类型惩罚而'为了存在而构建联合'。)

  

返回的值在char [4]中。然后我需要将其转换为uint32_t

只需使用memcpy或手动将字节移动到正确的位置,以防字节排序成为问题。好的编译器无论如何都可以优化它(是的,即使是对memcpy的调用)。

答案 1 :(得分:0)

  

我使用了工会来解决问题&#34;这似乎仍然不是100%好的,因为标准不能保证(这表明我只能依赖于在工会中最后修改的值)。

Endianess就是这个原因。具体而言,字节序列01 00 00 00可能意味着1或16,777,216。

正确执行操作的方法是停止尝试欺骗编译器为您执行转换并自行执行转换。

例如,如果char[4]是小端(最小字节优先),那么您将执行以下操作。

char[] buff = new char[4];
uint32_t result = 0;
for (int i = 0; i < 4; i++)
    result = (result << 8) + buff[i];

这会手动执行两者之间的转换,并且在进行数学转换时保证始终正确。

现在,如果您正在快速进行此转换,那么使用#if和您的架构知识来使用枚举来自动执行此操作可能是有意义的,如您所提到的,但这又是远离便携式解决方案。 (如果你不确定的话,你可以使用这样的东西作为你的后备)