dew*_*ald 81 c c++ bit-manipulation endianness bit
我读到结构中位字段的顺序是特定于平台的.如果我使用不同的特定于编译器的打包选项,这将保证数据在写入时以正确的顺序存储吗?例如:
struct Message
{
unsigned int version : 3;
unsigned int type : 1;
unsigned int id : 5;
unsigned int data : 6;
} __attribute__ ((__packed__));
Run Code Online (Sandbox Code Playgroud)
在具有GCC编译器的英特尔处理器上,字段在显示时显示在内存中.Message.version是缓冲区中的前3位,然后Message.type是.如果我找到各种编译器的等效结构包装选项,这将是跨平台的吗?
Ste*_*non 97
不,它不是完全便携的.结构的包装选项是扩展,并且它们本身不是完全可移植的.除此之外,C99§6.7.2.1第10段说:"单位内的位域分配顺序(从高阶到低阶或低阶到高阶)是实现定义的."
例如,即使是单个编译器也可能根据目标平台的字节顺序不同地放置位字段.
Jos*_*hua 42
从编译器到编译器,位字段差异很大,抱歉.
对于GCC,大端机器首先布置了大端,而小端机器首先布置了小端.
K&R说"结构的相邻[bit-]字段成员在依赖于实现的方向上被打包到依赖于实现的存储单元中.当另一个字段后面的字段不适合时...它可以在单元之间分割,或者单元可以是宽度为0的未命名字段强制填充......"
因此,如果您需要与机器无关的二进制布局,则必须自己完成.
最后一个语句也适用于填充引起的非位域 - 但是所有编译器似乎都有一些强制结构字节打包的方法,正如我在GCC中发现的那样.
Mic*_*urr 33
应避免使用位域 - 即使在同一平台上,它们在编译器之间也不是很容易移植.来自C99标准6.7.2.1/10 - "结构和联合说明符"(C90标准中有类似的措辞):
实现可以分配足够大的任何可寻址存储单元来保持位域.如果剩余足够的空间,则紧跟在结构中的另一个位字段之后的位字段将被打包到相同单元的相邻位中.如果剩余的空间不足,则是否将不适合的位域放入下一个单元或重叠相邻单元是实现定义的.单元内的位域分配顺序(高阶到低阶或低阶到高阶)是实现定义的.未指定可寻址存储单元的对齐.
你不能保证位字段是否会"跨越"一个int边界,你不能指定一个位域是从int的低端开始还是从int的高端开始(这与处理器是否是独立的无关) big-endian或little-endian).
喜欢位掩码.使用内联(甚至宏)来设置,清除和测试位.
字节序正在谈论字节顺序而不是位顺序.如今,99%确定位订单是固定的.但是,使用位域时,应该计算字节序数.请参阅下面的示例.
#include <stdio.h>
typedef struct tagT{
int a:4;
int b:4;
int c:8;
int d:16;
}T;
int main()
{
char data[]={0x12,0x34,0x56,0x78};
T *t = (T*)data;
printf("a =0x%x\n" ,t->a);
printf("b =0x%x\n" ,t->b);
printf("c =0x%x\n" ,t->c);
printf("d =0x%x\n" ,t->d);
return 0;
}
//- big endian : mips24k-linux-gcc (GCC) 4.2.3 - big endian
a =0x1
b =0x2
c =0x34
d =0x5678
1 2 3 4 5 6 7 8
\_/ \_/ \_____/ \_____________/
a b c d
// - little endian : gcc (Ubuntu 4.3.2-1ubuntu11) 4.3.2
a =0x2
b =0x1
c =0x34
d =0x7856
7 8 5 6 3 4 1 2
\_____________/ \_____/ \_/ \_/
d c b a
Run Code Online (Sandbox Code Playgroud)
大多数时候,可能是,但不要把农场押在上面,因为如果你错了,你就会失去巨大的.
如果你确实需要具有相同的二进制信息,则需要使用位掩码创建位域 - 例如,对Message使用无符号短(16位),然后使用versionMask = 0xE000来表示三个最高位.
结构中的对齐存在类似的问题.例如,Sparc,PowerPC和680x0 CPU都是big-endian,Sparc和PowerPC编译器的常见默认设置是将结构成员对齐在4字节边界上.但是,我用于680x0的一个编译器仅在2字节边界上对齐 - 并且没有更改对齐的选项!
因此对于某些结构,Sparc和PowerPC上的大小相同,但在680x0上较小,并且一些成员在结构中处于不同的内存偏移中.
这是一个项目,我的工作就一个问题,因为运行于Sparc服务器进程将查询客户机,并找出它是大端,并假设它可能只是喷二元结构在网络上和客户端可以应付.这在PowerPC客户端上运行良好,并且在680x0客户端上大量崩溃.我没有编写代码,并且花了很长时间才找到问题.但是一旦我这么做就很容易解决.
| 归档时间: |
|
| 查看次数: |
68427 次 |
| 最近记录: |