核心就是:一个是数据字节顺序,一个内存存储地址顺序
为什么会有 大端模式和小端模式的区别的,这是由于编码的原因,因此,先大致介绍一下编码:
在之前的计算机中,一般都是用 ASCII 码,但是其只能表示 2^7 = 128 个不同的字符。(仅仅只能表示 英文)
随着计算机的发展,计算机要表示不同国家的文字,比如 中文、泰文等等。那么这个时候,ASCII 编码就完全不够用了。
所以,引入了 Unicode编码,其目的就是:把世界上的文字都映射到一套字符空间中
为了表示Unicode字符集,有下面 3 种(确切的说是 5 种)Unicode的编码方式
对于不同的编码,当一个文本文件,用 二进制形式打开的时候,最前面的几个字节就可以区分是什么编码
编码错误的根本原因,在于编码方式和解码方式的不统一,也就是说,当一个机器用的是这种编码方式。到这个文件到了另一个机器时,如果另一个机器用的解码方式对不上,就会导致显示乱码
一般的编程语言,没有声明其 编码方式。而 html 会在一开始声明其 编码方式,这样子方便其他机器知道编码方式后,用对应的解码来处理。所以 html 才用于 网页的设计,因为会在不同机器上传输。
字节序问题,就是,当我们一个东西,用多个 byte 表示的时候。
又因为内存地址,一个地址只表示 1 byte。
所以,对于内存地址而言,地址一般是从 低地址到高地址的。
而,对于多个 byte 表示的那个东西,也会有所谓的字节顺序。举个例子,0x12345678
,这是由 4 byte 表示的一个数(也可以是一个字符之类的),那么一般其 字节序就是,高到低为:12 34 56 78
(十六进制表示,2位就是 8 bit,即 1 byte)
那么对于,字节表示的内容,当存放在 地址的时候,就会有两种顺序,我们假设内存地址都是 低地址到高地址。同样的,还是 0x12345678
,此时有两种情况
情况一:
此时,对于 高位字节位置 对应在 低地址。这和我们直接读的时候,很符合(称为 大端模式 – 低地址对应高字节位置)
这个读的时候,由于低地址对应高字节,而我们平时都是高字节读起,所以其存储习惯符合我们直接读取:0x 12 34 56 78 = 0x12345678
低地址 高地址
----------------------------------------------------------------------------->
| 12 | 34 | 56 | 78 |
情况二:
此时,对于 高字节位置,对应在高地址。也就是说,低地址 对应 低字节位置,称为 小端模式。这个时候读字节结果的时候,需要从 高字节到低字节,即 高地址到低地址:0x 12 34 56 78 = 0x12345678
低地址 高地址
----------------------------------------------------------------------------->
| 78 | 56 | 34 | 12 |
上面也大概介绍了一下 字节序的问题。
一般字节序的问题,是由于系统自身存储的问题,一般分为两大 CPU派系:那就是Motorola的PowerPC系列CPU和Intel的x86系列CPU。PowerPC系列采用big endian方式存储数据,而x86系列则采用little endian方式存储数据。
big endian是指低地址存放最高有效字节(MSB)
little endian则是低地址存放最低有效字节(LSB)
记忆方式:内存地址,永远都是从 低地址开始的
大端模式:低地址 对应 高字节(大 – 高字节)
小端模式:低地址 对应 低字节(小 – 低字节)
数字0x12345678在两种不同字节序CPU中的存储顺序如下所示
大端模式
此时,对于 高位字节位置 对应在 低地址。这和我们直接读的时候,很符合(称为 大端模式 – 低地址对应高字节位置)
这个读的时候,由于低地址对应高字节,而我们平时都是高字节读起,所以其存储习惯符合我们直接读取:0x 12 34 56 78 = 0x12345678
低地址 高地址
----------------------------------------------------------------------------->
| 12 | 34 | 56 | 78 |
小端模式
此时,对于 高字节位置,对应在高地址。也就是说,低地址 对应 低字节位置,称为 小端模式。这个时候读字节结果的时候,需要从 高字节到低字节,即 高地址到低地址:0x 12 34 56 78 = 0x12345678
低地址 高地址
----------------------------------------------------------------------------->
| 78 | 56 | 34 | 12 |
C/C++ 语言编写的程序里数据存储顺序是跟编译平台所在的CPU相关的,而JAVA编写的程序则唯一采用big endian方式来存储数据。
试想,如果你用C/C++ 语言在x86平台下编写的程序跟别人的JAVA程序互通时会产生什么结果?就拿上面的0x12345678
来说,你的程序传递给别人的一个数据,将指向0x12345678
的指针传给了JAVA程序,由于JAVA采取big endian方式存储数据,很自然的它会将你的数据翻译为0x78563412。因此,在你的C程序传给JAVA程序之前有必要进行字节序的转换工作。
所有网络协议也都是采用big endian的方式来传输数据的。所以有时我们也会把big endian方式称之为网络字节序。当两台采用不同字节序的主机通信时,在发送数据之前都必须经过字节序的转换成为网络字节序后再进行传输。
// 若处理器是Big_endian的,则返回0;若是Little_endian的,则返回1。
// 解释:union 是共用体,即里面的所有数据是共用同一块内存空间的,由占据字节最大的绝对
// 这个里面有 int 和 char,int 是 4 字节,char 是 1 字节。所以 union w 总共占据 4 字节的内存
// 我们将 c.a = 1,那么根据 int 的,那么 a 的值为 0x 00 00 00 01
// 假设顺序是 低地址到高地址
// 如果是 00 00 00 01 的存,那就是,低地址对应高字节,大端模式,那么此时对于 char b 而言,只占据 1 字节,所以 b = 0x00,所以 b = 0 为 大端模式
// 如果是 01 00 00 00 的存,那就是,低地址对应低字节,小端模式,那么此时对于 b = 0x01,即 b = 1 为小端模式
// 所以 return (c.b == 1); 当 返回 1(真),就是小端模式;返回 0,为 大端模式
int checkCPU()
{
{
union w
{
int a;
char b;
} c;
c.a = 1;
return (c.b == 1);
}
}
请问下列代码的输出结果有可能是哪些()?
#include
#include
union X
{
int32_t a;
struct
{
int16_t b;
int16_t c;
};
};
int main(){
X x;
x.a=0x20150810;
printf("%x,%x\n",x.b,x.c);
return 0;
}
A. 2015,810
B. 50810,201
C. 810,2015
D. 20150,810
解析
首先先根据 union 的定义,是由其中最大的字节占用据欸的那个 union 占用的内存空间
union X
{
int32_t a; // 那就是 32 bit,也就是 4 字节
// struct 是里面的字节占用空间之和(还有其偏移),这个总共占据 4 字节
struct
{
int16_t b;
int16_t c;
};
};
// 所以 union 总共占据 4 字节
那么当 a = 0x20150810,根据大端和小端的情况,有两种可能:
当是大端时,假设都是从低地址到高地址,那么此时是,低地址对应高字节,那么 20 15 08 10
,而 b 和 c 来分这个 4 字节,一人两个字节,那么此时 b 为 20 15
,注意此时是 低地址对高字节,所以 b = 0x2015
;同理 c = 0x0810
;
当是小端时,低地址对应低字节,那么此时 a 存储在 内存地址上的为 10 08 15 20
,那么分配给 b 和 c,此时 b 为 10 08
,而低地址对应低字节
答案
A、C
假设 A 给 B 发送数据,其中 A 是 大端模式,B是 小端模式
此时,A中的数据是 0x12345678
因为 A 是 大端模式,那么就是,低地址对应高字节,即 12 34 56 78
,此时,发送给 B(从 A 发给 B,从低地址发,B 也从低地址收),那么 B 接收到的为 12 34 56 78
但是,B 是小端模式,即 低地址对应低字节,那么 B 收到的数据为 0x78563412
主机字节序一般都是小端(绝大多数,少部分也是大端存储的),网络字节序是大端存储的。
因此,对于从 主机中,发送给另一个主机,一般先将该主机的变为 网络字节序,然后另一个主机接收到 网络字节序后,再转换为 自己的字节序
#include
1.htons():把unsigned short类型从主机序转换到网络序(h:host,主机;n:net,网络;s:unsigned short,16位短整数)
2.htonl():把unsigned long类型从主机序转换到网络序(l:unsigned long,32位长整数)
3.ntohs():把unsigned short类型从网络序转换到主机序
4.ntohl():把unsigned long类型从网络序转换到主机序
如果主机序是小端字节序,这些函数将参数做相应的大小端转换然后返回,如果主机序是大端字节序,这些函数将不对参数做转换,将参数原封不动地返回。
5.uint32_t htonl(uint32_t hostint32);
功能:将32位主机字节序数据转换为网络字节序数据
参数:hostint32,需要转换的32位主机字节序数据,uint32_t为32位无符号整型
返回值:若成功,返回网络字节序的值
6.uint16_t htons(uint16_t hostint16);
功能:将16位主机字节序数据转换成网络字节序数据
参数:hostint16,需要转换的16位主机字节序数据,uint16_t为16为无符号短整型
返回值:若成功,返回网络字节序的值
7.uint32_t ntohl(uint32_t netint32);
功能:将32位网络字节序数据转换为主机字节序数据
参数:netint32,需要转换的32位网络字节序数据,uint32_t为32位无符号整型
返回值:若成功,返回主机字节序的值
8.uint16_t ntohs(uint16_t netint16);
功能:将16位网络字节序数据转换成主机字节序数据
参数:netint16,需要转换的16位网络字节序数据,uint16_t为16为无符号短整型
返回值:若成功,返回主机字节序的值