一.大小端介绍
“大端”和“小端”表示多字节值的哪一端存储在该值的起始地址处。小端存储在起始地址处,即是小短字节序;大端存储在起始地址处,即是大端字节序。
大端存储模式:数据的低位保存在内存中的高地址中,数据的高位保存在内存中的低地址中;
小端存储模式:数据的低位保存在内存中的低地址中,数据的高位保存在内存中的高地址中;(STM32属于小端模式)
常用的x86结构是小端模式,而keil C51则为大端模式,很多的arm、dsp都为小端模式,有些arm处理器还可以由硬件来选择是大端模式还是小端模式。
int a=1;//一个int占4字节
二.为什么会有大小端存储模式
1.cpu和编译器不同
在计算机系统中,我们是以字节为单位的,每个字节都对应着一个地址单元,但是在C语言中除了8bit的char之外,还有16bit的short型、32bit的long型(要看具体的编译器)。另外,对于位数大于8位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如何将多个字节安排的问题,因此,就导致了大端存储模式和小端存储模式。
2.网络字节序
网络上传输的数据都是字节流。对于一个多字节数值,在进行网络传输的时候,先传递哪个字节,也就是说,当接收端收到第一个字节的时候,它将这个字节作为高字节还是低字节来处理,是一个比较又意义的问题。
UDP/TCP/IP协议规定:把接收到的第一个字节当作高位字节看待,这就要求发送端发送的第一个字节是高位字节,而在发送端发送数据时,发送的第一个字节是该数值在内存中的起始地址处对应的那个字节,也就是说,该数值在内存中的起始地址处对应的那个字节就是要发送的第一个高位字节。由此可见,多字节数值在发送之前,在内存中是以大端法存放的。所以说,网络字节序是大端字节序。
三.如何判断CPU使用大端存储还是小端存储
1.类型降低
#include<stdio.h>int main(void)
{short int a = 0x1234;unsigned char b=a;printf("%xH\n",b);//34H}
2.使用指针,类型强转
#include<stdio.h>
int main(void)
{short a = 0xff00;char* b = (char*)&a;if (*b == 0xff){printf( "大端");}else{printf( "小端");}return 0;
}
3.采用共用体
#include<stdio.h>
int checkCPU()
{union w{int a;char b;}c;c.a = 1;return (c.b == 1);
}
int main(void)
{if (checkCPU())printf("小端\n");elseprintf("大端\n");
}
联合体union的存放顺序是所有成员都从低地址开始存放。如果处理器是大端存储模式,则c.b=0,checkCPU()函数返回0。
四.应用实列
#include<stdio.h>int main(void)
{//sizeof(unsigned short)=2//sizeof(struct mybitfields)=2//sizeof(short)=2//sizeof(test)=2struct mybitfields{unsigned short a:4;unsigned short b:5;unsigned short c:7;};struct mybitfields test;int i;test.a = 2;test.b = 3;test.c = 0;i = *((short *)&test);printf("%d\n",i);return 0;}
上例的声明方式是把一个short(也就是一块16位内存)分成三部分,各部分的大小分别是4位、5位、7位,赋值语句i = *((short *)&test);就是把上面的16位内存转换成short类型进行解释。变量a的二进制表示为0000000000000010,取其低四位是0010;变量b的二进制表示为0000000000000011,取其低五位是00011;变量c的二进制表示为0000000000000000,取其低七位是0000000。所以合成后得到0000000000110010,即十进制50。
五.如何进行大小端的转换
#include<stdio.h>//int型
int swapInt32(int intvalue)
{int temp = 0;temp = ((intvalue & 0x000000FF) << 24)|((intvalue & 0x0000FF00) << 8) |((intvalue & 0x00FF0000) >> 8) |((intvalue & 0xFF000000) >> 24) ;return temp;
}//unsigned short型
unsigned short swapshort16(unsigned short shortvalue)
{return ((shortvalue & 0x00FF) << 8) | ((shortvalue & 0xFF00) >> 8);}//float型
float swapfloat32(float floatvalue)
{typedef union SWAP_UNION{float unionFloat;int unionInt;}SWAP_UNION;SWAP_UNION swapUnion;swapUnion.unionFloat = floatvalue;swapUnion.unionInt=swapInt32(swapUnion.unionInt);}//double型
void swapdouble64(unsigned char * pin , unsigned char *pout)
{for (int i=0; i < 8; i++ ){pout[7-i] = pin[i];}
}
int main(void)
{unsigned short x=0x12;unsigned short y=swapshort16(x);printf("%xH\r\n",y);return 0;
}
1001 1101 1101 1001//40409D
调用swapshort16()转换成
1101 1001 1001 1101//55709D
六.绝对地址相关
1.对绝对地址赋值
*(unsigned int *)0x100000 = 1234;
2.让程序跳转到绝对地址0x100000去执行
2.1 将0x100000强制转换成函数指针
( void (*)() )0x100000
2.2 再调用它
* ( ( void (*)() )0x100000 ) ();
用typedef可以更直观些
typedef void(*)() voidFuncPtr;
*((voidFuncPtr)0x100000)();