本文上部分转自:http://www.cnblogs.com/zhengyuhong/p/3667869.html
下半部分为自己所写。
本文基于知识共享署名-非商业性使用 3.0 许可协议进行许可。欢迎转载、演绎,但是必须保留本文的署名林羽飞扬,若需咨询,请给我发信
struct T{ unsigned short a:5; unsigned short b:5; unsigned short c:6; }; int main(int argc, char**){ struct T t; t.a=16; t.b=4; t.c=0; short i = *(short*)&t; cout<<i; return 0; }
上述程序中,在32位小端模式、大端模式下输出何值?
来分析一下,小端模式下,低地址先存放高位域成员,lowAddr->highAddr : c|b|a,
c | b | a | |||||||||||||
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
转换为short,00000000100010000=0x0090=144,
大端模式下,低地址先存放低位域成员,lowAddr->highAddr:a|b|c
a | b | c | |||||||||||||
1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
转换为short,1000000100000000=0x1100,以补码形式表示整型,所以0x1100=-32512
---------------------------------华丽的分割线-----------------------------------------
还不够清楚明白是吧?再看如下例子:
//当你这样测试时,指针读取了1个字节出来,读的是a的后8bit int a = 0x0f0f0f01; char * p = (char*) & a; if(*p == 0x01) printf("little endian\n"); else printf("big endian\n");
以上程序在X86机器上输出结果是little endian没错。
继续看:
/* 当你这样测试时,指针读取了2个字节出来,读的是a的后16bit。 但并不是0x010f这样的打印出结果,而是打印出0x0f01。这是因为在打印出来时,又被机器给转成正常序给显示了。 下例讲X86的小端模式。 内存实地址:小 -> 大 a的32位数据 01 0d 0e 0f (是逆序的没错) (1)如果打印出a,会是这样的:0x0f0e0d01,这是因为在输出的时候已经自动做了转换。 (2)如果截取a的地址的前8位,打印出来会是0x01。 (3)如果截取a的地址的前16位,打印出来会是0x0d01。 为什么会这样?因为同第1点一样,输出的时候已经帮你自动作了转换。所以你是永远看不到它输出0x010d的,这只是它的存储方式。 */ int a = 0x0f0e0d01; short * p = (short*) & a; if(*p == 0x010f) printf("little endian\n"); else printf("big endian\n");
请注意:
如果你创建了1个结构体/数组来测试,当使用形如 printf("%d",&a)这样的语句来打印某变量的地址时,输出的是线性地址,也就是虚地址,这是一个连续的,也就是按照地址从小->大,逐个创建变量的。
但是内存实存上并不是这样存储的,这里面还关系到内存的页机制,但是线性地址的一个变量的地址大小是肯定会映射到实存上同大小且连续的空间上的,比如int a,这个变量a在线性地址上占用连续4字节,映射到实存上仍然占用连续4字节。如果有int a[2],那么a[1]打印出的地址大小肯定大于a[0]的,因为这是线性地址。但是存储方式按照字节存储的,所以就会有大小端模式。
单个字节如何存储就不用去管了,也没有什么实际价值。重要的是,我们在进行位操作时,是以平时写二进制那样的操作,比如11001010可以往左移或右移,读数仍然按照我们的习惯。但是如果有10101010 00000000 16字节的变量,存储在内存中就不一定是10101010 00000000了,也可能是00000000 10101010,但是进行位操作时仍然没有区别。