首页 > 解决方案 > C - 大端结构与小端结构相互转换

问题描述

我有两个具有相同数据成员的结构。(一个是big_endian结构,另一个是little_endian)现在我必须与它们相互转换。但是当我编码时,我发现有很多重复的代码,几乎没有变化。如何在不重复代码的情况下将这些代码更改为更优雅?(重复代码意味着这些代码可能相似,例如mode == 1and mode == 2,它们只是在分配位置上有所不同。它看起来不优雅但有效。)

这是我的代码:

#pragma scalar_storage_order big-endian

typedef struct {   
    int a1;    
    short a2;    
    char a3;    
    int a4;    
} test_B;

#pragma scalar_storage_order default

typedef struct {
    int a1;    
    short a2;    
    char a3;    
    int a4;    
} test_L;


void interconvert(test_L *little, test_B *big, int mode) {
    // if mode == 1 , convert little to big    
    // if mode == 2 , convert big to little    
    // it may be difficult and redundant when the struct has lots of data member!    
    if(mode == 1) {
        big->a1 = little->a1;
        big->a2 = little->a2;
        big->a3 = little->a3;
        big->a4 = little->a4;
    }
    else if(mode == 2) {
        little->a1 = big->a1;
        little->a2 = big->a2;
        little->a3 = big->a3;
        little->a4 = big->a4;
    }
    else return;
}

注意:以上代码必须在gcc-7 或更高版本上运行,因为#pragma scalar_storage_order

标签: cgccoptimizationstruct

解决方案


发布了一个答案,建议使用 memcpy 解决此问题,但该答案已被删除。实际上,如果使用正确,这个答案是正确的,我想解释一下原因。

正如他指出的那样,OP 指定的#pragma 是核心:

注意:由于#pragma scalar_storage_order,上述代码必须在 gcc-7 或更高版本上运行

来自OP的结构:

#pragma scalar_storage_order big-endian
typedef struct {   
    int a1;    
    short a2;    
    char a3;    
    int a4;    
} test_B;

表示指令“test_B.a2=256”在属于 a2 成员的两个连续字节中写入,分别为 1 和 0。这是 big-endian。类似的指令“test_L.a2=256”将改为存储字节 0 和 1(小端序)。

以下内存:

memcpy(&test_L, &test_B, sizeof test_L)

会使 test_L.a2 的字节等于 1 和 0,因为那是 test_B.a2 的 ram 内容。但是现在,以小端模式读取 test_L.a2,这两个字节表示 1。我们写入 256 并读回 1。这正是想要的转换。

要正确使用此机制,只需在一个结构中写入 memcpy() 在另一个结构中,然后逐个成员读取另一个结构就足够了。大端变成小端,反之亦然。当然,如果打算详细说明数据并对其应用计算,那么了解数据的字节序很重要;如果它与默认模式匹配,则在计算之前不必进行任何转换,但必须稍后应用转换。相反,如果传入的数据与处理器的“默认字节序”不匹配,则必须先对其进行转换。


编辑在下面的OP评论之后,我进行了更多调查。我看了一下这个https://gcc.gnu.org/onlinedocs/gcc/Structure-Layout-Pragmas.html

好吧,有 3 个 #pragma 可供选择字节布局:big-endianlittle-endiandefault。前两个之一等于最后一个:如果目标机器是little-endian,则default表示little-endian;如果是 big-endian,则默认为 big-endian。这不仅仅是合乎逻辑的。

因此,在 big-endian 和 default 之间执行 memcpy() 在 big-endian 机器上没有任何作用;这也是合乎逻辑的。好的,我更强调 memcpy() 本身绝对没有做任何事情:它只会将数据从以某种方式处理的 ram 区域移动到以另一种方式处理的另一个区域。仅当完成正常的成员访问时,才会对这两个不同的区域进行不同的处理:这里来播放#pragma scalar_storage_order。正如我之前写的,重要的是要知道哪些字节序有数据进入程序。例如,如果它们来自 TCP 网络,我们知道那是大端;更一般地说,如果它来自“程序”之外并尊重协议,我们应该知道字节序有什么。

要将字节序转换为另一种字节序,应该使用littlebig,而不是默认值,因为该默认值肯定等于前两者之一。


又一个编辑

受到评论和使用在线编译器的 Jamesdlin 的刺激,我也尝试这样做。在这个 url http://tpcg.io/lLe5EW 有一个演示,分配给一个结构的成员,memcpy 给另一个,并阅读它,字节序转换就完成了。就这样。


推荐阅读