问题引出: 计算机大端小端之争。
0x12 | 0x34 | 0x56 | 0x78
0x78 | 0x56 | 0x34 | 0x12
大端小端的Pro and con
1)Little-Endian就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。
2) Big-Endian就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。
2) Big-Endian就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。
数字0x12345678 在大端小端的存储方式
1)大端模式:
低地址 -----------------> 高地址0x12 | 0x34 | 0x56 | 0x78
2)小端模式:
低地址 ------------------> 高地址0x78 | 0x56 | 0x34 | 0x12
内存地址 | 小端模式存放内容 | 大端模式存放内容 |
0x4000 | 0x78 | 0x12 |
0x4001 | 0x56 | 0x34 |
0x4002 | 0x34 | 0x56 |
0x4003 | 0x12 | 0x78 |
小端: 强制类型转化时候比较方便; 大端判断符号类型比较方便。
实际中:
操作系统使用小端,网络程序使用大端;
硬件
Big endian: PowerPC, Sun, IBM;
Little endian: Intel, DEC
ARM既可以大端也可以小端。
如何判断是大端还是小端
void Test_1()
{
unsigned int a = 0x1234; /*unsigned int to check if 0x 12 34 or 0x 34 12*/
char* b = (char *) &a; /*取unsigned int的地址,然后将它强制转化为char型*/
if(b == 0x12) /*如果是0x 12 34大端,强制转化了则b 为0x12*/
{
printf("Big endian\n");
}else /*如果是0x 34 12小端,强制转化了则b 为0x34*/
{
printf("Little endian and %x\n", *b);
}
}
{
unsigned int a = 0x1234; /*unsigned int to check if 0x 12 34 or 0x 34 12*/
char* b = (char *) &a; /*取unsigned int的地址,然后将它强制转化为char型*/
if(b == 0x12) /*如果是0x 12 34大端,强制转化了则b 为0x12*/
{
printf("Big endian\n");
}else /*如果是0x 34 12小端,强制转化了则b 为0x34*/
{
printf("Little endian and %x\n", *b);
}
}
其他办法,因为C语言的联合的各个成员会共享一块内存,所以是否可以通过联合体来判断。
typedef unsigned char BYTE;
void Test_2()
{
unsigned int num = 0; /*num = 0x 00 00 00 00*/
unsigned int *p = #
*(BYTE *)p = 0xff; /*to check where the system put the 0x 00 00 00 ff or 0x ff 00 00 00*/
if(num == 0xff) /* 0x 00 00 00 ff*/
{
printf("This endian of cpu is little\n");
}else /* 0x ff 00 00 00*/
{
printf("This endian of cpu is big\n");
}
}
{
unsigned int num = 0; /*num = 0x 00 00 00 00*/
unsigned int *p = #
*(BYTE *)p = 0xff; /*to check where the system put the 0x 00 00 00 ff or 0x ff 00 00 00*/
if(num == 0xff) /* 0x 00 00 00 ff*/
{
printf("This endian of cpu is little\n");
}else /* 0x ff 00 00 00*/
{
printf("This endian of cpu is big\n");
}
}
看看linux 如何判断大端小端的
static union
{
char c[4];
unsigned long l;
}linuxEndianTest = {{'l','?','?','b'}};
{
char c[4];
unsigned long l;
}linuxEndianTest = {{'l','?','?','b'}};
#define LINUXENDIAN ((char) linuxEndianTest.l)
void Test_4()
{
printf("%c", LINUXENDIAN);
}
{
printf("%c", LINUXENDIAN);
}
实际的应用:
在单片机编程中,联合体和位域结合,可以实现对LED的控制
有时候既想操作整个字节 显示一个数,也想控制某位闪烁。
定义如下的联合体
typedef union uFLG{
uint8 Flg ; //定义整型,可以一次性操作
struct FLAG{ //位域定义,可以一次性操作一个位
uint8 Flg1 : 1;
uint8 Flg2 : 1;
uint8 Flg3 : 1;
uint8 Flg4 : 1;
uint8 Flg5 : 1;
uint8 Flg6 : 1;
uint8 Flg7 : 1;
uint8 Flg8 : 1;
}tFlg;
}uFlg;
uint8 Flg ; //定义整型,可以一次性操作
struct FLAG{ //位域定义,可以一次性操作一个位
uint8 Flg1 : 1;
uint8 Flg2 : 1;
uint8 Flg3 : 1;
uint8 Flg4 : 1;
uint8 Flg5 : 1;
uint8 Flg6 : 1;
uint8 Flg7 : 1;
uint8 Flg8 : 1;
}tFlg;
}uFlg;
//-------uF1-------------------
#define uFg1 uF1.Flg //可以操作位
#define F_LED1 uF1.tFlg.Flg1
#define F_LED2 uF1.tFlg.Flg2
#define F_LED3 uF1.tFlg.Flg3
#define uFg1 uF1.Flg //可以操作位
#define F_LED1 uF1.tFlg.Flg1
#define F_LED2 uF1.tFlg.Flg2
#define F_LED3 uF1.tFlg.Flg3
uFg1 = 0x88;
F_LED1 = 0x01;
网络协议中,不同的报文类型PackageType,不同的报文内容PackageContent,使用同一个结构表示。
typedef union tagPackageContent
{
PackageContent1 content1;
PackageContent2 content2;
PackageContent3 content3;
PackageContent3 content3;
};
typedef struct PackageStructure {
Byte type;
tagPackageContent content;
}