有没有办法为C或C ++结构强制执行特定的字节序?

我已经看到了关于结构的字节序的一些问题和答案,但它们是关于检测系统的字节序,或者在两个不同的字节序之间转换数据。

但是,如果有一种方法可以强制执行给定结构的特定字节顺序,那么我现在想要的是什么。 是否有一些好的编译器指令或其他简单的解决方案,除了重写操作在位域上的许多宏的整个事情?

一般的解决方案会很好,但我也会对特定的gcc解决方案感到满意。

编辑:

感谢所有评论指出为什么强制执行endianness不是一个好主意,但在我的情况下,这正是我需要的。

大量数据由特定处理器生成(永远不会改变,它是带有自定义硬件的嵌入式系统),并且必须由在未知处理器上运行的程序(我正在处理的)读取。 对数据进行字面评估会非常麻烦,因为它包含数百种不同类型的结构,这些结构非常庞大且深入:它们中的大多数都有许多其他巨大的结构层。

改变嵌入式处理器的软件是不可能的。 源是可用的,这就是为什么我打算使用该系统的结构而不是从头开始并按字节方式评估所有数据。

这就是为什么我需要告诉编译器它应该使用哪个字节序,无论效率与否都无关紧要。

它不一定是字节序的真正变化。 即使它只是一个接口,物理上所有东西都是在处理器自己的字节序中处理的,但我完全可以接受。

我通常处理这个的方式是这样的:

 #include  // for ntohs() etc. #include  class be_uint16_t { public: be_uint16_t() : be_val_(0) { } // Transparently cast from uint16_t be_uint16_t(const uint16_t &val) : be_val_(htons(val)) { } // Transparently cast to uint16_t operator uint16_t() const { return ntohs(be_val_); } private: uint16_t be_val_; } __attribute__((packed)); 

同样适用于be_uint32_t

然后你可以像这样定义你的结构:

 struct be_fixed64_t { be_uint32_t int_part; be_uint32_t frac_part; } __attribute__((packed)); 

关键是编译器几乎肯定按照你编写它们的顺序排列字段,所以你真正担心的是big-endian整数。 be_uint16_t对象是一个知道如何根据需要在big-endian和machine-endian之间透明转换的类。 像这样:

 be_uint16_t x = 12; x = x + 1; // Yes, this actually works write(fd, &x, sizeof(x)); // writes 13 to file in big-endian form 

实际上,如果使用任何相当不错的C ++编译器编译该代码段,您应该会发现它会将一个大端“13”作为常量发出。

使用这些对象,内存中表示是big-endian。 因此,您可以创建它们的数组,将它们放在结构中等等。但是当您对它们进行操作时,它们会神奇地转换为机器端。 这通常是x86上的单个指令,因此非常有效。 有一些情况你必须手工施放:

 be_uint16_t x = 37; printf("x == %u\n", (unsigned)x); // Fails to compile without the cast 

…但是对于大多数代码,您可以像使用内置类型一样使用它们。

派对有点晚了但是使用当前的GCC(在6.2.1上测试了它的工作原理和4.9.2在未实现的情况下测试),最后有一种方法可以声明结构应该保持X端字节顺序。

以下测试程序:

 #include  #include  struct __attribute__((packed, scalar_storage_order("big-endian"))) mystruct { uint16_t a; uint32_t b; uint64_t c; }; int main(int argc, char** argv) { struct mystruct bar = {.a = 0xaabb, .b = 0xff0000aa, .c = 0xabcdefaabbccddee}; FILE *f = fopen("out.bin", "wb"); size_t written = fwrite(&bar, sizeof(struct mystruct), 1, f); fclose(f); } 

创建一个文件“out.bin”,您可以使用hex编辑器(例如hexdump -C out.bin)进行检查。 如果支持scalar_storage_order属性,它将按此顺序包含预期的0xaabbff0000aaabcdefaabbccddee且没有漏洞。 遗憾的是,这当然是特定于编译器的。

不,我不这么认为。

Endianness是处理器的属性,指示整数是从左到右还是从右到左表示,它不是编译器的属性。

您可以做的最好是编写独立于任何字节顺序的代码。

不,没有这样的能力。 如果它存在可能导致编译器必须生成过多/低效的代码,因此C ++不支持它。

处理序列化的常用C ++方法(我假设是你要解决的问题)这是让结构以所需的确切布局保留在内存中,并以反序列化时保留字节序的方式进行序列化。

我不确定是否可以修改以下内容以满足您的目的,但在我工作的地方,我们发现以下内容在许多情况下非常有用。

当字节序很重要时,我们使用两种不同的数据结构。 一个是为了表示预期到达的方式。 另一个是我们希望它如何在内存中表示。 然后开发转换例程以在两者之间切换。

工作流程因此运作……

  1. 将数据读入原始结构。
  2. 转换为“原始结构”到“内存版本”
  3. 仅在“内存版本”中运行
  4. 完成操作后,将“内存版本”转换回“原始结构”并将其写出。

我们发现这种解耦很有用,因为(但不限于)……

  1. 所有转化仅限于一个地方。
  2. 使用“内存版本”时,关于内存对齐问题的问题更少。
  3. 它使得从一个拱门移植到另一个拱门变得更容易(更少的端点问题)。

希望这种解耦对您的应用程序也很有用。

一种可能的创新解决方案是使用像Ch这样的C解释器并强制使用endian编码。

Boost为此提供了endian缓冲区 。

例如:

 #include  #include  using namespace boost::endian; struct header { big_int32_buf_t file_code; big_int32_buf_t file_length; little_int32_buf_t version; little_int32_buf_t shape_type; }; BOOST_STATIC_ASSERT(sizeof(h) == 16U); 

也许不是一个直接的答案,但阅读这个问题可以有希望回答你的一些问题。

您可以使结构成为具有数据成员的getter和setter的类。 getter和setter的实现方式如下:

 int getSomeValue( void ) const { #if defined( BIG_ENDIAN ) return _value; #else return convert_to_little_endian( _value ); #endif } void setSomeValue( int newValue) { #if defined( BIG_ENDIAN ) _value = newValue; #else _value = convert_to_big_endian( newValue ); #endif } 

当我们从文件中读取结构时,我们有时会这样做 – 我们将其读入结构并在big-endian和little-endian机器上使用它来正确访问数据。

这个称为XDR的数据表示。 看看它。 http://en.wikipedia.org/wiki/External_Data_Representation

虽然对于您的嵌入式系统来说可能有点太多了。 尝试搜索您可以使用的已实现的库(检查许可限制!)。

XDR通常用于网络系统,因为它们需要一种以与Endianness无关的方式移动数据的方法。 虽然没有说它不能在网络之外使用。