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

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

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


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

这很好,gcc没有发出警告,我正试图“通过char* ”读取类型Tuint32_t ):

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

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

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

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

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

C和C ++是否相同(因为我读了一条评论,暗示它与2种语言不同)?


我使用union来“解决”这个问题,这仍然看起来不是 100%正常,因为标准并不保证(这表明我只能依赖于最后在union修改的值)。

所以,经过大量阅读,我现在更加困惑。 我想只有memcpy才是“好”的解决方案?


相关问题:

  • 什么是严格别名规则?
  • “解除引用类型 – 惩罚指针将打破严格别名规则”警告
  • 我是否正确理解C / C ++严格别名?
  • 严格的别名规则和’char *’指针

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

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

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

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

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

问题在于编译器使用规则来确定是否允许这样的优化。 在第二种情况下,您尝试通过不兼容的指针类型读取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的调用)。

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

Endianess就是这个原因。 具体地,字节序列01 00 00 00可以表示1或16,777,216。

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

例如,如果char[4]是little-endian(最小字节优先),那么你会做类似下面的事情。

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

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

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