大端 小端

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.youkuaiyun.com/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



### 大端模式及其在编程中的应用 #### 什么是大端和小模式 大端模式(Big-Endian)是指数据的高位字节保存在内存的低地址中,而数据的低位字节保存在内存的高地址中[^3]。例如,对于一个四字节整数 `0x12345678`,在大端模式下,其存储顺序为:低地址处存放 `0x12`,然后是 `0x34`、`0x56`,高地址处存放 `0x78`。 小模式(Little-Endian)则相反,它将数据的低位字节保存在内存的低地址中,而数据的高位字节保存在内存的高地址中[^3]。同样以 `0x12345678` 为例,在小模式下,低地址处存放 `0x78`,然后是 `0x56`、`0x34`,高地址处存放 `0x12`。 #### 大端与小模式的应用场景 在网络通信中,通常使用大端模式作为标准字节序,称为网络字节序[^1]。这意味着在跨平台或跨设备的数据传输中,发送方需要将数据转换为大端格式,接收方则需将其转换回本地字节序。 在实际编程中,特别是在嵌入式系统或需要处理硬件特定问题时,了解当前系统的字节序非常重要。以下是一个判断当前系统字节序的 C 程序示例: ```c #include <stdio.h> int main() { int a = 1; // 在内存中表示为 0x00 00 00 01 if (*(char*)&a == 1) { // 取出第一个字节进行检查 printf("小\n"); } else { printf("大端\n"); } return 0; } ``` #### 编程中的具体应用 在多字节数据类型(如整型、浮点型等)的存储和传输过程中,必须考虑字节序的影响。例如,在二进制文件读写或网络通信中,如果发送方和接收方的字节序不同,则可能导致数据解析错误。 以下是将整数从主机字节序转换为网络字节序的一个 C 示例: ```c #include <arpa/inet.h> #include <stdio.h> int main() { uint32_t host_order = 0x12345678; uint32_t network_order = htonl(host_order); // 主机字节序转网络字节序 printf("Host Order: 0x%08X, Network Order: 0x%08X\n", host_order, network_order); return 0; } ``` #### 不同平台的字节序 大多数现代 x86 和 x86_64 架构的计算机使用小模式。然而,某些嵌入式系统或特定架构(如 PowerPC、SPARC)可能使用大端模式。因此,在开发跨平台应用程序时,应始终明确数据的字节序,并在必要时进行转换。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值