关于单字节的字节序的问题
一直认为 Big_endian, Little_endian 只有在多字节的数据中才有意义.今天看 linux 网络部分的代码 关于 ip头 的定义如下:
#if defined(__LITTLE_ENDIAN_BITFIELD)
__u8 ihl:4,
version:4;
#elif defined (__BIG_ENDIAN_BITFIELD)
__u8 version:4,
ihl:4;
#else
很纳闷啊, 单字节的数据也有字节序的问题?
我在 little 下是 0x12, 难道在 big 下是 0x21
我做了个实验, 将 0x1234 用二进制打印出来:
00110100 00010010 -- 0x34 0x12
然后将 0x1234 htons 以后 再打印出来:
00010010 00110100 -- 0x12 0x34
很显然 单字节并没有因为字节序的不同而不同, 那他原代码中为什么会如此写呢? 一个是机器的endian,一个是网络的endian。 说字节序只在多字节时才有意义是因为我们多数时候是使用2字节或4字节值,bit序也是有的,但CPU和网络之间会自动处理,所以编程时不关心。但IP头部的值并不都是字节的整数倍,这样的话就要考虑了。
你打印的话当然是看不出区别的,因为指令中已经自动处理了。你可以分别在Big-endian和Little-endian的计算机上用位域试一试,能看出区别来。以前做网络程序时经常要避免位域,就是比较容易出错。
[ 本帖最后由 Cyberman.Wu 于 2009-7-14 16:58 编辑 ] 这个问题有时候挺让人郁闷的,其实关键在于一个标准如何统一的问题,因为大端小端已是无法逃避的问题了。
根据C语言标准里,“An implementation may allocate any addressable storage unit large enough to hold a bitfield.
If enough space remains, a bit-field that immediately follows another bit-field in a
structure shall be packed into adjacent bits of the same unit.”
关键什么叫相邻?
我们还是用硬件描述语言的思路去理解这个问题吧。
其实大端和小端并不是天壤之别,拓扑来看,不过是某些接线顺序的问题,
比如verilog,命一个16bit的wire,你会怎么命?
wire sample;
还是
wire sample;
无论怎么命吧,
assign sample = 16'h1234;
在两种形式下,sample的每个bit将会是什么?
于是我们再考虑,按照两种方式设计的硬件(假设上面的那个是大端,下面这个的是小端)
这个sample直接接1个16bit的ram的数据总线。
那么
struct {
unsigned char a;
unsigned char b:4;
unsigned char c:4;
} x;
这个x此时从数据总线里出来,假设就是前面的那个16'h1234,
小端的时候,
x.a代表sample,自然是0x34
s.b因为是相邻,所以代表sample,自然是0x2
s.c代表sample,自然是0x1
而大端的时候,
x.a代表sample,自然是0x12
s.b因为是相邻,所以代表sample,自然是0x3
s.c代表sample,自然是0x4
数字设计的时候我经常被这些东西搞晕掉。
$ cat 1.c
#include <stdio.h>
union {
unsigned short m;
struct xx{
unsigned char a:7;
unsigned char b:5;
unsigned char c:4;
} n;
struct xxx{
unsigned short a:7;
unsigned short b:5;
unsigned short c:4;
} k;
}x;
int main()
{
x.m=0x1294;
printf("%hhx\n%hhx\n%hhx\n",x.n.a,x.n.b,x.n.c);
printf("%hx\n%hx\n%hx\n",x.k.a,x.k.b,x.k.c);
return 0;
}
$ gcc 1.c
$ ./a.out
14
12
0
14
5
1
恩,知道原因了,这里的x应该是4个字节。k.b是不能跨两个unsigned char的。
[ 本帖最后由 cjaizss 于 2009-7-15 22:09 编辑 ]
页:
[1]