C中的Endian独立性

时间:2011-11-28 06:11:12

标签: c microcontroller

在用C编写的微控制器项目中,我们定义了以下宏来访问多字节变量的不同字节(4字节long):

#define BYTE_0(var)         (*((unsigned char*) &var))
#define BYTE_1(var)         (*(((unsigned char*) &var) + 1))
#define BYTE_2(var)         (*(((unsigned char*) &var) + 2))

BYTE_0()访问最低有效字节,依此类推。这是因为我们发现如果我们需要分别访问多字节变量的不同字节(8位微信息),使用上面的代码访问字节会在汇编中产生更少的代码行。由于代码存储器大小仅为15K,因此几个字节有时很宝贵。

我们使用的micro是little-endian。我想知道我们是否将代码移植到另一个是big-endian架构的微代码上面,上面的代码是否可行?换句话说,C标准是否保证(*((unsigned char*) &var))将给出var的最低有效字节?

5 个答案:

答案 0 :(得分:4)

您的宏不起作用,它假设小端架构。在您的代码中,C标准不保证任何内容。与字节无关的代码通常使用逐位运算符编写,因为无论分配ls字节的位置如何,它们的行为方式都相同。

some_long & 0xFF由C标准保证,无论字节顺序如何都会给出ls字节,而(uint8_t*)&some_long依赖于字节序。

此链接详细解答了您的问题:http://www.ibm.com/developerworks/aix/library/au-endianc/ 使用按位移位和按位AND执行类似于清单12中的宏,并且它将是可移植的。

答案 1 :(得分:3)

不,这就是endianness的含义。您的代码不适用于具有相反字节顺序的计算机。

我甚至不确定这种手动优化是否重要。也许更好的编译器可以更好地优化......

答案 2 :(得分:2)

#define BYTE_0(var)         (*((unsigned char*) &var))

会给你一个与处理器/内存控制器性质相关的字节,甚至不一定是变量var的一个字节。理想情况下,如果var为0x12345678,您希望在某些系统上看到0x12,在其他系统上看到0x78。

#define BYTE_0(var)         (var&0xFF)

为任何系统提供var的最低有效字节 ASSUMING var在每个系统上都相同。

完成清单。

#define BYTE_0(var)         ((var>> 0)&0xFF)
#define BYTE_1(var)         ((var>> 8)&0xFF)
#define BYTE_2(var)         ((var>> 16)&0xFF)
#define BYTE_3(var)         ((var>> 24)&0xFF)

不要使用位域而不是移位和屏蔽,位域不会从编译器移植到编译器,端点相同或不同。位域是“实现定义的”,你能想到的每一个可能搞乱的东西都可以在编译器中找到。

小心试图扭转你的问题并从字节构建一个变量:

var = (b3<<24)|(b2<<16)|(b1<<8)|(b0<<0);

如果b3,b2,b1,b0被定义为8位变量,则编译器在移位之前不必将它们提升为32位。在某些系统/编译器上,上面的代码会产生将四个字节放入32位变量的预期效果。但是在其他系统中,var = b0就是上面的代码所要做的,因为b1是一个8位变量,它离开8,你留下零,同样转移b2 16和b3 24,你最终得到

var = 0 | 0 | 0 | b0;

我更喜欢

var = 0;
var <<= 8; var |= b3;
var <<= 8; var |= b2;
var <<= 8; var |= b1;
var <<= 8; var |= b0;

var = b3;
var <<= 8; var |= b2;
var <<= 8; var |= b1;
var <<= 8; var |= b0;

哪个端口相当不错。并且优化器应该为您提供与使用typedef或位域解决方案的单行C相似/相同的代码。

答案 3 :(得分:1)

不,不会。你已经明白为什么小端比大端更好地工作的原因。

还要考虑转换为不同的整数大小不需要任何指针算法。

假设long总是4个字节也是错误的。不幸的是,x64的标准是LP64,即int被遗忘为4字节整数。

答案 4 :(得分:0)

如果您想要一种可移植的方式来访问4字节整数的字节,您可以使用#define根据体系结构更改宏,

#ifdef LITTLE_ENDIAN
#define OFFSET_0 0
#define OFFSET_1 1
#define OFFSET_2 2
#else
#define OFFSET_0 3
#define OFFSET_1 2
#define OFFSET_2 1
#endif



#define BYTE_0(var)         (*(((unsigned char*) &var) + OFFSET_0))
#define BYTE_1(var)         (*(((unsigned char*) &var) + OFFSET_1))
#define BYTE_2(var)         (*(((unsigned char*) &var) + OFFSET_2))

或者,如果你的架构支持超过1位的移位,这是最好的IMO。

#define BYTE_0(var)         ((var) & 0xFF)
#define BYTE_1(var)         (((var) >> 1 * CHAR_BIT) & 0xFF)
#define BYTE_2(var)         (((var) >> 2 * CHAR_BIT) & 0xFF)

甚至适用于非lvars(例如表达式,文字的结果),并且是可移植的。