【问题标题】:Does casting remove endian dependency in C/C++?强制转换是否会删除 C/C++ 中的字节序依赖?
【发布时间】:2012-05-18 17:49:44
【问题描述】:

即如果我们将名为 arr 的 C 或 C++ 无符号字符数组转换为 (unsigned short*)arr 然后分配给它,结果是否与机器字节序无关?

旁注 - 我看到了关于 IBM 和其他地方关于 SO 的讨论:

unsigned char endian[2] = {1, 0};
short x;
x = *(short *) endian;

...表示 x 的值将取决于字节序的布局,因此取决于机器的字节序。这意味着取消引用数组是依赖于字节序的,但是分配给它呢?

*(short*) endian = 1;

无论字节顺序如何,是否所有未来的短解引用都保证返回 1?


阅读回复后,我想发布一些背景信息:

在这个结构中

struct pix { 
    unsigned char r; 
    unsigned char g;
    unsigned char b; 
    unsigned char a; 
    unsigned char y[2]; 
 }; 

用 unsigned short y 替换 unsigned char y[2] 没有个体差异,但是如果我将这些结构组成一个数组并将其放入另一个结构中,那么我注意到容器结构的大小往往是“无符号短”版本更高,因此,由于我打算制作一个大数组,因此我选择了 unsigned char[2] 以节省空间开销。我不知道为什么,但我想在内存中对齐 uchar[2] 更容易。

因为我需要对变量 y 进行大量数学运算,这意味着它是一个单一的短长度数值,我发现自己为了避免单独访问 uchar 字节而将其转换为 short 很多...排序一种避免丑陋的字节特定数学的快速方法,但后来我想到了字节顺序以及如果我只是将所有内容都转换为这样,我的数学是否仍然正确

*(unsigned short*)this->operator()(x0, y0).y = (ySum >> 2) & 0xFFFF;

...这是一个程序中的一行,它在二维数组中平均 4 个相邻的邻居,但关键是我有一堆需要对 uchar[2] 字段进行操作的操作作为一个单一的短片,我试图找到最轻的(即,每次我需要访问或分配时都没有基于字节序的 if-else 语句),与短片无关的字节序无关方式。

【问题讨论】:

    标签: c casting variable-assignment endianness dereference


    【解决方案1】:

    由于严格的指针别名,它是未定义的行为,所以它可能是任何东西。如果您对联合执行相同操作但答案是否定的,则结果取决于机器字节序。

    【讨论】:

    • “这是未定义的行为,所以它可能是任何东西” - 是的,但实际上我相信实现普遍支持别名 unsigned char 作为另一种类型(它们是必需的 支持将另一种类型别名为unsigned char,当然),因为否则几乎不可能实现malloc,或者用户代码实现自己的“内存分配器”,将一大堆字符分成更小的块用于其他类型。所以唯一需要担心的是对齐。
    • @Steve:虽然你描述的是真的,但严格来说,违反有效类型编译器扩展;另外,请记住,程序员有责任处理对齐要求,如果您使用联合黑客,编译器会透明地为您处理...
    • 同意。作为另一种选择:如果您同时使用memcpy 进行读取和写入,则不会违反严格别名,并且 char 数组的对齐方式无关紧要。
    【解决方案2】:

    short 的每个可能值都有一个所谓的“对象表示”[*],它是一个字节值序列。当short 类型的对象保存该值时,该对象的字节保存该值序列。

    您可以将字节顺序视为对象表示依赖于实现的方式之一:地址最低的字节是保存值的最高有效位还是最低有效位?

    希望这能回答您的问题。假设您已经安全地将1 的有效对象表示形式作为短路写入某个内存,当您从同一内存中读回它时,您将再次获得相同的值,而不管1 的对象表示实际上是什么在那个实现中。尤其是不管字节顺序。但正如其他人所说,您必须避免未定义的行为。

    [*] 或者,在奇异的架构上,同样的值可能有不止一个对象表示。

    【讨论】:

      【解决方案3】:

      是的,所有未来的取消引用也将返回 1:由于 1 在类型 short 的范围内,它最终会在未修改的内存中,并且一旦它存在就不会在你背后改变。

      但是,代码本身违反了有效类型:将unsigned char[2] 作为short 访问是非法的,如果您的架构不支持非对齐访问并且您特别不走运,则可能会引发SIGBUS

      但是,任何对象的字符访问始终是合法的,您的代码的可移植版本如下所示:

      short value = 1;
      unsigned char *bytes = (unsigned char *)&value;
      

      value 是如何存储在内存中的当然仍然是实现定义的,即如果不进一步了解架构,您将无法知道以下将打印什么:

      assert(sizeof value == 2); // check for size 2 shorts
      printf("%i %i\n", bytes[0], bytes[1]);
      

      【讨论】:

      • 如果你 幸运,我会说它会提高 SIGBUS。一些 ARM 芯片会在未对齐的访问中静默地读取或写入“不正确”的值。
      • 我明白了。感谢您的可移植性建议。在这个结构 struct pix { unsigned char r;无符号字符 g;无符号字符 b;无符号字符 a;无符号字符[2]; };用短 y 替换 char y[2] 没有个体差异,但是如果我将这些结构组成一个数组并将其放入另一个结构中,那么我注意到容器结构的大小对于短版本往往更高,所以因为我打算做一个大数组,所以我选择了 char[2]。但由于我需要用这个变量做大量的数学运算,我发现自己做空了很多......
      • @Cindeselia:您可能应该就这些大小差异提出一个单独的问题,并显示“另一个结构”的完整代码。如果存在大小差异,则应该可以通过重新排序成员或使用编译器特定的指令来控制结构打包和对齐来获得较小的大小y 变短.
      • @Cindeselia:你在用速度换取规模,因为y 作为short 的访问会错位,这在大多数架构上成本更高;但是,具有较小的结构对缓存更友好,这可能会或可能不会抵消此成本;按照史蒂夫的建议,问另一个问题可能是个好主意……
      猜你喜欢
      • 2011-09-22
      • 2017-04-27
      • 2012-12-14
      • 2016-01-03
      • 2012-03-11
      • 2010-10-29
      • 1970-01-01
      • 2017-10-05
      • 1970-01-01
      相关资源
      最近更新 更多