http://blog.sina.com.cn/s/blog_6ffd3b5c0100mc4z.html
1. 小端
在bitstream的定义中,经常见到这样的定义
exam{
A:18 bit stream left bit first
B: 14 bit stream left bit first
}
在小端(little endian)系统中,上述32bit被读入内存,memory map如下
------------------------------------------------------------------------------------
| A7 ... A0 | A15 ... A8 | B5 ... B0 A17 A16 | B13 ... B6 |
byte0 byte1 byte2 byte3
low address --------------------------------> high address
-------------------------------------------------------------------------------------
NOTE:该处A0代表线路上最先到达的bit,A17代表最后到达.
可见小端系统是一个移位性质的寄存器,先到达的bit放在byte内的高位地址。(所谓小端地址的定义是:most significant byte in high address。所以 0x01 0x02 0x03 0x04,代表的数据是0x04030201.同样的道理,在字节内部,物理地址高的bit代表高位。所以如果一个binary stream在字节中从左到右的顺序是 10001010,那么它代表的byte数据应该是0x51,而不是0x8A.)
可见,属于同一个域A的数据在物理地址上面并不连续了。为了处理这个问题,我们必须把这32bit数据变换为小端Byte顺序,既
------------------------------------------------------------------------------------
| B13 ... B6 | B5 ... B0 A17 A16 | A15 ... A8 | A7 ... A0 |
byte3 byte2 byte1 byte0
low address --------------------------------> high address
-------------------------------------------------------------------------------------
这样一来,同一个域的数据在物理上就连续了。使用C语言bit field的定义
struct{
UINT32 B:14;
UINT32 A:18;
}
这样 B 对应了这个32bit结构的低14bits, A 对应了高18bits。编译器只需要简单的截断移位就可以处理这两个域了。
2. 大端
大端系统正好相反,在byte顺序上是高位数据对应least significant byte。在byte内部靠近高位地址的bit对应least significant bit.
数据到达内存的时候
------------------------------------------------------------------------------------
| A0 ... A7 | A8 ... A15 | A16 A17 B0 ... B5 | B6 ... B13 |
byte0 byte1 byte2 byte3
low address --------------------------------> high address
-------------------------------------------------------------------------------------
大段在物理地址上和传输顺序(文档描述)一模一样,比较容易理解。这也是大端结构设计的初衷。
于是只需要在bit field声明如下结构:
struct{
UINT32 A:18;
UINT32 B:14;
}
这样就一目了然了。
3.总结
这些内容如果不是经常使用非常容易混淆,因为小端需要有两个转换,首先是byte顺序,其次是bit顺序。使用的时候最好在pc上多做实验。
对于小端,我之前理解的有个误区,比如0x12345678
我认为仅仅只字节的位置倒置,其实是这个数字的bit全部倒置,而c对一个字节内的操作是不可见的,就是你以为是正序的,其实它是逆序的。
比如,int c = 5;
while(c) {
cout<<(c&1)<<endl;
c =c >>1;
}
我们发现字节是正序排列的,即0101。其实在内存中bit是逆序排列的,1010,只是操作系统对我们隐藏了对bit的操作。
例如,对于这个题目:
typedef struct bitstruct
{
int b1:5;
int :2;
int b2:2;
} bitstruct;
void main()
{
bitstruct b;
memcpy(&b, "EMC EXAMINATION", sizeof(b));
printf("%d,\n", sizeof(b)); //输出4
printf("%d, %d\n", b.b1, b.b2); //输出5,-2
}
在内存中存储的是,0x45 4d 43 20
并且在每个字节内的bit是逆序排列的
内存分布
1 |
0 |
1 |
0 |
0 |
0 |
1 |
0 |
1 |
0 |
1 |
1 |
0 |
0 |
…… |
E |
M |
|||||||||||||
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
…… |
b.b1是 10100,对其再次逆序,得00101,得5
b.b2 = 01,对其再次逆序是 10,取其补码是-2
http://blog.csdn.net/ermuzhi/article/details/7940280
99规定int、unsigned int和bool可以作为位域类型,但编译器几乎都对此作了扩展,允许其它类型类型的存在。使用位域的主要目的是压缩存储,其大致规则为:
1) 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;
2) 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
3) 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式,Dev-C++采取压缩方式;
4) 如果位域字段之间穿插着非位域字段,则不进行压缩;
5) 整个结构体的总大小为最宽基本类型成员大小的整数倍。
和结构体一样,位结构体也是按照成员的最大长度字节来对齐分配空间的。
测试:
struct test
{
char a:1;
char :2;
long b:3;
char c:2;
};
test t1;
int len=sizeof(t1); //len=4,前两个位域共用一个char空间,按第三个位域long扩展为4字节空间,最后一个位域占用一个char空间,按最长位域空间扩展为4字节。
structtest
{
char f1 : 3;
short f2 : 4;
char f3 : 5;
};
test t1;
int len=sizeof(t1); //len=2,第一位域占用一个char空间,按第二个位域short扩展为2字节空间,最后一个位域占用一个char空间,按最长位域空间扩展为2字节。
如果位域上的整形范围值是0,则下个位域从新的字节开始,前一字段后面空出的所有字节都不使用(即使是同类型的位域),如:
structbs
{
unsigned a:4;
unsigned :0; // 空域
unsigned b:4; // 从新字节开始存放
unsigned c:4;
} ;
sizeof(unsigned)== 4
上面这个位域定义中,a占第一字节的4位,第一个字节的后4位以及后面的3个字节都填0表示不使用,b从新的字节开始,占用4位,c占用4位。上面位结构体大小为:8
struct test
{
char a:1;
char :2;
char b:3;
long c:2;
};
test t1;
int len=sizeof(t1); //len=4
struct test
{
char a:1;
char :2;
char b:3;
char c:2;
};
test t1;
int len=sizeof(t1); //len=1
{// test2
union V {
struct X {
unsigned char s1:2;
unsigned char s2:3;
unsigned char s3:3;
} x;
unsigned char c;
} v;
v.c = 100;
printf("%d", v.x.s3);
}
结果:3,从低位开始分配位空间
#include <iostream>
using namespace std;
struct A
{
char t:4;
char k:4;
unsigned short i:8;
unsigned long m;
}
;
main()
{
struct A a;
a.t= 'b ';
cout < < a.t < < endl;
}
a.t = 'b';效果相当于 a.t= 'b' & 0xf;
'b' --> 01100010
'b' & 0xf -->>00000010
所以输出Ascii码为2的特殊字符
下一个例子:
union Test {
char a[5];
short b;
};
int main()
{
Test t;
t.a[0] = 256;
t.a[1] = 255;
t.a[2] = 0x81;
t.a[3] = 2;
printf("%d",t.b);
return 0;
}
首先256用int存储,所以a[0]只能取其后面一个字节,为0
最终a的前两个字节的内存分布是:
ff00
取其补码为-256