关于计算机界的大小端问题有著名的CPU两大派系,PowerPC系列采用大端(big endian)的方式存储数据,而X86系列则采用小端(little endian)方式存储数据。很显然如果你的程序只运行在PowerPC系列的CPU上,你完全可以不管什么是little endian,但是如果你PowerPC上的程序要和X86上的程序打交道,那么你就必须进行转换才能相互识别,
我们又该怎样去理解它呢?
- 我们应该知道数据在计算机中是以二进制形式存储的,1个字节有8位,每1位的值只能是0或者1;
- 我们还知道一个4位的二进制数和1位的十六进制数是1-1对应的,那么1个字节就可以拆分成2位的十六进制数;
- 我们还应该知道像0x1234abcd这样一个16进制数在内存中它是以4个字节存储的,既然这个数据在内存中跨越了多个字节,那么它肯定是有顺序的,我们叫它字节序。【一般操作系统都是小端,而通讯协议是大端的】
- 这里的端指的是数据的尾端,大指的是高内存地址,小指的是低内存地址
一般人的书写习惯:
- 数据左边的为高位,右边的为低位,
- 地址则相反:地址左边的为低位,右边的为高位
例如: 数据16位数据:0x12345678,12为数据的高位,78位数据的低位
大端模式:
数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位往低位放;这和我们的阅读习惯一致。
例如:四字节的数据:0x12345678,
地址:低->高,低地址存放高位,高地址存放低位
存储为:
小端模式,是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中,这种存储模式将地址的高低和数据位权有效地结合起来,高地址部分权值高,低地址部分权值低。
例如:四字节的数据0x12345678,
地址:低->高,高地址存放高位,低地址存放低位
存储为:
只需记住与我们阅读习惯一致的为大端模式。
- 大端:把高位存储到低地址里面,把低位存储到高地址里面
- 小端:把低位存储到低地址里面,把高位存储到高地址里面
CPU大小端判断
判断计算机是大端还是小端存储
- 字符指针判断
int 类型占4个字节,而 char 类型占一个字节的,如果我们把 &int 强转为 char* 类型的指针,只会保存一个字节的数据,那么我们只需要判断 char 存储的数据和 int 存储第一个字节的数据是否是一致即可判断。
代码实现:
#include <stdio.h>
#include <stdlib.h>
// 小端模式:LITTLE; 大端模式:BIG
int main()
{
int a = 1;
char pc = *(char*)(&a);
if (pc == 1)
printf("LITTLE\n");
else
printf("BIG\n");
return 0;
}
- 联合体判断
由于联合体所有数据共享一块地址空间,存放数据的所有成员都是从低地址开始存放,所以我们可以在联合体内定义一个 int 和一个 char 类型变量。然后在外部实例化的时候创建 int 变量,用 char 变量调用,相当于隐式类型转化。如果结果为1,则低字节存放在低地址,既是小端机器,反之大端机器。
union A{
char c;
int i;
}s;
int main(){
s.i = 0x12345678;
if(s.c == 0x78)
printf("小端:Little\n");
else
printf("大端:Big\n");
return 0;
}
- 为什么不能直接通过强制类型转换?
直接将 int 类型转换成 char 类型,得到的是 int 类型的低8个 bit 位。(至于这低8个 bit 位,如果存储在高位就拿高位,存储在低位就拿低位)。就跟大端小端没有关系了,大端小端是存进内存的方式。
int main(){
int i = 0x12345678;
char c = i; // 方法不合理
if(c == 0x78)
printf("小端:Little\n");
else
printf("大端:Big\n");
return 0;
}
大端与小端的优势
二者无所谓优势,无所谓劣势,各自优势便是对方劣势
- 大端模式:符号位的判定固定为第一个字节,容易判断正负。
- 小端模式:强制转换数据不需要调整字节内容,1、2、4字节的存储方式一样
数组在大端小端情况下的存储:
以unsigned int value = 0x12345678为例,分别看看在两种字节序下其存储情况,
- 我们可以用unsigned char buf[4]来表示value:
- Big-Endian: 低地址存放高位,如下:
高地址
---------------
buf[3] (0x78) -- 低位
buf[2] (0x56)
buf[1] (0x34)
buf[0] (0x12) -- 高位
---------------
低地址
Little-Endian: 低地址存放低位,如下:
高地址
---------------
buf[3] (0x12) -- 高位
buf[2] (0x34)
buf[1] (0x56)
buf[0] (0x78) -- 低位
--------------
低地址
知识点例题:
- temp=0X12345678;
低地址---------->高地址【大端模式】:
0X12|0X34|0X56|0X78|
低地址---------->高地址【小端模式】:
0X78|0X56|0X34|0X12|
- 假设变量x类型为int,位于地址0x100处,它的十六进制为0x01234567,地址范围为0x100~0x103字节,其内部排列顺序依赖于机器的类型。
大端法从首位开始是:0x100: 01, 0x101: 23, 0x102: 45, 0x103: 67
小端法从首位开始是:0x100: 67, 0x101: 45, 0x102: 23, 0x103: 01
- 将int型数组强制转换为char*,再求strlen。(涉及大小端~~)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(){
int a[2000];
char *p = (char*)a;
for( int i = 0; i < 2000; i++){
a[i] = -1 - i;
}
printf("%d\n", strlen(p));
return 0;
}
正确答案: 1020 1020 1020(代码运行出来也是这个)
答案解析:
首先要明白负数在内存中的存储方式,还要知道int和char各占几位(都是最基本的啦)。这里很容易知道:
-
a[0] = -1 内存中应当是:11111111 11111111 11111111 11111111
-
a[1] = -2 内存中应当是:11111111 11111111 11111111 11111110
-
a[2] = -3 内存中应当是:11111111 11111111 11111111 11111101
-
……
-
a[255] = -256 内存中应当是: 11111111 11111111 11111111 00000000
当程序计算strlen( p )的时候遇到8个0就停止了,所以是255 * 4 + 3 = 1023.
为什么结果是1020呢?(PS:我的cpu是Intel的,Intel的cpu一般都是小端存储)这就涉及到内存的存储问题了。
众所周知,内存存储分为大端小端,大端就是我们人类理解的这样,将高位写在前面,将地位写在后面,小端存储则正好相反,所以a[255] = -256 在内存中的表示形式是: 00000000 11111111 11111111 11111111,这就是为什么答案是1020。当然了不同的机器会有不同,如果笔试的时候注明一下,应该效果会更好。