目录
转载地址:https://blog.csdn.net/qq_34201858/article/details/104110767
一、为什么会有大小端模式之分呢?
因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一个字节,一个字节为8bit。但是在C语言中除了8bit的char之外,还有16bit的short型,32bit的int型。另外,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如果将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。
数据在计算机中是以二进制形式存储的,1个字节有8位,每1位的值只能是0或者1;一个4位的二进制和十六进制是一一对应的,那么1个字节就可以拆分成2位的十六进制数;像0x1234abcd这样一个16进制数在内存中它是以4个字节存储的,既然这个数据在内存中跨越了多个字节,那么它肯定是有顺序的,我们叫它字节序。
大端指的就是把字节序的尾端(0xcd)放在高内存地址,而小端指的就是把字节序的尾端(0xcd)放在低内存地址。
例如一个16bit的short型 val,在内存中的地址为0x0010,val的值为0x1122,那么0x11为高字节,0x22为低字节。
对于大端模式,就将0x11放在低地址中,即0x0010中,0x22放在高地址中,即0x0011中。小端模式,刚好相反。
二、什么是大端和小端?
大端模式:是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中。
小端模式:是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中。
假如32位宽(uint32_t)的数据0x12345678,从地址0x08004000开始存放:
再结合一张图进行理解:
从上面表格、图可以看得出来,大小端的差异在于存放顺序不同。
小端模式:强制转换数据不需要调整字节内容,1、2、4字节的存储方式一样。
大端模式:符号位的判定固定为第一个字节,容易判断正负。
总结:大端小端没有谁优谁劣,各自优势便是对方劣势。
现代PC大多采用小端字节序,因此小端字节序又被称为主机字节序。
大端字节序也被称为网络字节序,它给所有接收数据的主机提供了一个正确解释收到的格式化数据的保证。
三、数组在大端小端情况下的存储
以unsigned int value = 0x12345678为例,分别看看在两种字节序下其存储情况,我们可以用unsigned char buf[4]来表示value。
1.大端模式下
2.小端模式下
四、如何判断大小端呢?
代码的实现就是初始化一个16进制的类型的数据,然后把它放在一个char类型的数组中,由于十六进制的数据一位代表四个bit位char型是8个bit位,那么十六进制的两位占一个char位,那么就可以把数据位分离。
通过上面的例子,我们调用内存窗口可以看到0x12345678在内存中是以78 56 34 12的方式存放的,可以看到变量i每个字节的内容,从而轻易的判断出它是小端存储。
写程序判断其实也是这样的思路:想办法取出一个字节的内容,就可以知道是哪种存储方式。比如,图上,i变量是4个字节,我们只要取出它的第一个字节内容,如果是78,则说明是小端存储;反之是大端。
关于“取出一个字节的内容”,有三种办法:
法一:直接法
-
int main()
-
{
-
-
int a =
0x12345678;
-
char i = a;
-
printf(
"%x", i);
-
}
定义一个十六进制的数据,数据类型为int型,之后定义一个char类型的数据,int数据类型的大小为四个字节,而char类型的数据为一个字节,所以将int类型的数据赋值给char时会丢失三个字节的数据,char类型中存储的是int类型中低地址的数据,这时候char类型获取的数据输出之后,如果输出的是12那就说明你低地址位置的数据是12,那就说明你的数据是大端存储,如果输出的结果是78那当前条件下就是小端存储。
法二:指针法
-
int main()
-
{
-
int i =
0x1122;
-
char* p = (
char*)& i;
-
if (p[
0] ==
0x22 && p[
1] ==
0x11) {
-
cout <<
"little endian" << endl;
-
}
-
else
if (p[
0] ==
0x11 && p[
1] ==
0x22) {
-
cout <<
"big endian" << endl;
-
}
-
}
*p就是p[0],把变量的地址强制类型转换为char*,这样就可以每次取出一个字节的内容,因为char的大小就是1个字节,p[0]和p[1]都表示一个char类型。
可以看到,我们将int*类型的 &i强制转换为了char*类型,但值没有改变(地址)
该地址存储的值也未改变,p就是表示的这个地址,但p是char*类型的变量,因此可以用p[0]p[1]去取,每一个p[]就是两个十六进制的数(也就是一个字节)。在我的电脑上,p[0]就是0x22,p[1]就是0x11。
法三:联合体法
-
int main(){
-
union
-
{
-
int value;
-
char union_bytes[
sizeof(
int) ];
-
} test;
-
test.value =
0x0102;
-
if ( ( test.union_bytes[
0 ] ==
1 ) && ( test.union_bytes[
1 ] ==
2 ) )
-
{
-
printf(
"big endian\n" );
-
}
-
else
if ( ( test.union_bytes[
0 ] ==
2 ) && ( test.union_bytes[
1 ] ==
1 ) )
-
{
-
printf(
"little endian\n" );
-
}
-
else
-
{
-
printf(
"unknown...\n" );
-
}
-
}
在联合体中定义一个char 类型的变量和int类型的变量,利用二者所占同一段存储空间,可以通过引用联合体变量中的成员访问char 类型的数据,取出一个字节的内容。
在union中所有的数据成员共用一个空间,而且是从低位开始占用,同一时间只能储存其中一个数据成员,所有的数据成员具有相同的起始地址,共用体变量的内存空间大小是该变量中某个占用空间最大的那个成员所占的空间。即上述的union虽然定义了两个成员,但其实这个union只占用了4个字节(32位机器中 int 所占的空间大小),往 value 成员赋值(value完整是0x00000102),然后读取 union_bytes ,union_bytes[0]就是value的第一个字节,union_bytes[1]就是value的第二个字节,依此类推。