数据在内存中的存储方式( Big Endian和Little Endian的区别 )(x86系列则采用little endian方式存储数据)
https://www.cnblogs.com/renyuan/archive/2013/05/26/3099766.html
1.故事的起源
“endian”这个词出自《格列佛游记》。小人国的内战就源于吃鸡蛋时是究竟从大头(Big-Endian)敲开还是从小头(Little-Endian)敲开,由此曾发生过六次叛乱,其中一个皇帝送了命,另一个丢了王位。
我们一般将endian翻译成“字节序”,将big endian和little endian称作“大尾”和“小尾”。
2.什么是Big Endian和Little Endian?
在设计计算机系统的时候,有两种处理内存中数据的方法。一种叫为little-endian,存放在内存中最低位的数值是来自数据的最右边部分(也就是数据的最低位部分)。 比如某些文件需要在不同平台处理,或者通过Socket通信。这方面我们可以借助ntohl(), ntohs(), htonl(), and htons()函数进行格式转换,
个人补充:一个操作数作htonl或ntohl结果不一定相同,当机器字节序跟网络字节序刚好是仅仅big endian和little endian的区别时是相同的。
3. 如何理解Big Endian和Little Endian
举个例子:
int a = 1;
a这个数本身的16进制表示是0x00 00 00 01
在内存中怎么存储呢?
如果你的CPU是intel x86架构的(基本上就是通常我们说的奔腾cpu),那么就是0x01 0x00 0x00 0x00 , 这也就是所谓的little-endian, 低字节存放在内存的低位.
如果你的CPU是老式AMD系列的(很老很老的那种,因为最新的AMD系列已经是x86架构了), 它的字节序就是big-endian, 其内存存储就是 0x00 0x00 0x00 0x01在内存中从高字节开始存放。
现在世界上绝大多数的CPU都是little-endian。
4. 了解big-endian和little-endian有什么作用?
一个重要的作用就是了解在网络上不同的机器间的数据如何传输。 假设我们在网络上有两台机器A和B, 其中A为little-endian,B为big-endian 机器A要传输上面的整数a给机器B,如何传输呢? 过程是这样的:
机器A先把a在内存中的四个字节0x 01 0x00 0x00 0x00转化为网络字节序0x00 0x00 0x00 0x01,然后一个字节一个字节(从0x00到0x01)喂到网络上去 ,然后机器B从网络上一个字节一个字节地取出四个字节0x00 0x00 0x00 0x01后又会转化为本地字节序 0x00 0x00 0x00 0x01后放入内存。因而B正确地得到了来自A的数据a 。如果数据缺少在网络上的字节序转换的话,情况会怎样呢?
机器A先把a由在内存的四个字节0x 01 0x00 0x00 0x00 一个字节一个字节地喂到网络上,然后机器B从网络上一个字节一个字节地收到0x 01 0x00 0x00 0x00并放入到内存中, B认为他收到了0x01000000, 也就是十进制数1677216,这显然是错误的.
5.如何判断系统是Big Endian还是Little Endian?
在/usr/include/中(包括子目录)查找字符串BYTE_ORDER(或_BYTE_ORDER, __BYTE_ORDER),确定其值。这个值一般在endian.h或machine/endian.h文件中可以找到,有时在feature.h中,不同的操作系统可能有所不同。一般来说,Little Endian系统BYTE_ORDER(或_BYTE_ORDER,__BYTE_ORDER)为1234,Big Endian系统为4321。大部分用户的操作系统(如windows, FreeBsd,Linux)是Little Endian的。少部分,如MAC OS ,是Big Endian 的。本质上说,Little Endian还是Big Endian与操作系统和芯片类型都有关系。
在ARM体系中,每个字单元包含4个字节单元或者两个半字单元。在字单元中,4个字节哪一个是高位字节,哪一个是低位字节则有两种不同的格式:big-endian和little-endian格式。在小端模式中,低位字节放在低地址,高位字节放在高地址;在大端模式中,低位字节放在高地址,高位字节放在低地址。
如果将一个32位的整数0x12345678(如用UltraEdit打开某个文件看到的第一行头四个字节是:"00000000h:12 34 56 78")存放到一个整型变量(int)中,这个整型变量(文件内容)采用大端或者小端模式在内存中的存储由下表所示。
地址偏移 |
大端模式 |
小端模式 |
0x00 |
12 |
78 |
0x01 |
34 |
56 |
0x02 |
56 |
34 |
0x03 |
78 |
12 |
对于文件内容 0x12345678,把前面("12")的看为高端字节,后面("78")的看为低端字节,那么可以使用"高高低低"(Little Endian),"高低高低"(Big Endian)的口诀。直观的区分,如果发现内存的内容和文件的内容在顺序上以4个字节颠倒,那么他就是Little Edian。实现Big Endian和Little Endian主要是由编译器指定的,通常是在CCFLAG 加参数,如: -DENDIAN_LITTLE,设定编译为小端字节。实际中用Trace 32可以用Memory Dump查看内存内容,和写入文件比较后判断为大端还是小端。
如果将一个16位的整数0x1234存放到一个短整型变量(short)中。这个短整型变量在内存中的存储在大小端模式由下表所示。
地址偏移 |
大端模式 |
小端模式 |
0x00 |
12 |
34 |
0x01 |
34 |
12 |
由上表所知,采用大小模式对数据进行存放的主要区别在于在存放的字节顺序,大端方式将高位存放在低地址,小端方式将低位存放在低地址。
那么该如何判断CPU是大端模式还是小端模式呢??
在C语言中,联合体union的存放顺序是所有成员都从低地址开始存放的。利用这一特点,可以用联合体变量判断ARM或x86环境下,存储系统是是大端还是小端模式。
具体的代码如下:
#include "stdio.h"
int main()
{
union w
{
int a; //4 bytes
char b; //1 byte
} c;
c.a=1;
if (c.b==1)
printf("It is Little_endian!/n");
else
printf("It is Big_endian!/n");
return 1;
}
数据在存放到内存里的时候,有两种存放方式,即:Big Endian 和 Little Endian,这两个存取方式决定了内存存放数据的原则是 高高低低 原则 还是 高低低高 原则。
高高低低--内存中的高位存放数据的高位,内存中的低位存放数据的低位(Little Endian )
高低低高--内存中的高位存放数据的低位,内存中的低位存放数据的高位(Big Endian )
比如:我有一个数据,是0xA5A1,它在存放到内存中是怎样存放的呢?因为在我们平时的书写中,A5是高位,A1在低位,存放到内存中的时候,A1存放在0x4000这个位置,而A5存放在0x4001这个位置,高位存放在内存的高地址中,低位存放在低地址中,这种方式就是Little Endian 。
下面再粘一篇网友的总结的文章来详细阐述一下这两种方式的区别:
Big Endian 和 Little Endian 模式的区别(转载)
谈到字节序的问题,必然牵涉到两大CPU派系。那就是Motorola的PowerPC系列CPU和Intel的x86系列CPU。PowerPC系列采用big endian方式存储数据,而x86系列则采用little endian方式存储数据。那么究竟什么是big endian,什么又是little endian呢?
其实big endian是指低地址存放最高有效字节(MSB),而little endian则是低地址存放最低有效字节(LSB)。
用文字说明可能比较抽象,下面用图像加以说明。比如数字0x12345678在两种不同字节序CPU中的存储顺序如下所示:
Big Endian
低地址 高地址
----------------------------------------------------------------------------->
| 12 | 34 | 56 | 78 |
Little Endian
低地址 高地址
----------------------------------------------------------------------------->
| 78 | 56 | 34 | 12 |
从上面两图可以看出,采用big endian方式存储数据是符合我们人类的思维习惯的。
为什么要注意字节序的问题呢?你可能这么问。当然,如果你写的程序只在单机环境下面运行,并且不和别人的程序打交道,那么你完全可以忽略字节序的存在。但是,如果你的程序要跟别人的程序产生交互呢?尤其是当你把你在微机上运算的结果运用到计算机群上去的话。
在这里我想说说两种语言。C/C++语言编写的程序里数据存储顺序是跟编译平台所在的CPU相关的,而JAVA编写的程序则唯一采用big endian方式来存储数据。试想,如果你用C/C++语言在x86平台下编写的程序跟别人的JAVA程序互通时会产生什么结果?就拿上面的0x12345678来说,你的程序传递给别人的一个数据,将指向0x12345678的指针传给了JAVA程序,由于JAVA采取big endian方式存储数据,很自然的它会将你的数据翻译为0x78563412。因此,在你的C程序传给JAVA程序之前有必要进行字节序的转换工作。
所有网络协议也都是采用big endian的方式来传输数据的。所以有时我们也会把big endian方式称之为网络字节序。当两台采用不同字节序的主机通信时,在发送数据之前都必须经过字节序的转换成为网络字节序后再进行传输。ANSI C中提供了下面四个转换字节序的宏。
一道C语言的试题:请写一个C函数,若处理器是Big_endian的,则返回0;若是Little_endian的,则返回1。
解答:
int checkCPU()
{
{
union w
{
int a;
char b;
} c;
c.a = 1;
return (c.b == 1);
}
}
嵌入式系统开发者应该对Little-endian和Big-endian模式非常了解。采用Little-endian模式的CPU对操作数的存放方式是从低字节到高字节,而Big-endian模式对操作数的存放方式是从高字节到低字节。例如,16bit宽的数0x1234在Little- endian模式CPU内存中的存放方式(假设从地址0x4000开始存放)为:
内存地址
存放内容
0x4000
0x34
0x400
0x12
而在Big-endian模式CPU内存中的存放方式则为:
内存地址
存放内容
0x4000
0x12
0x4001
0x34
32bit宽的数0x12345678在Little-endian模式CPU内存中的存放方式(假设从地址0x4000开始存放)为:
内存地址
存放内容
0x4000
0x78
0x4001
0x56
0x4002
0x34
0x4003
0x12
而在Big-endian模式CPU内存中的存放方式则为:
内存地址
存放内容
0x4000
0x12
0x4001
0x34
0x4002
0x56
0x4003
0x78
联合体union的存放顺序是所有成员都从低地址开始存放,解答利用该特性,轻松地获得了CPU对内存采用Little-endian还是Big-endian模式读写。
数据在内存中的存储方式( Big Endian和Little Endian的区别 )(x86系列则采用little endian方式存储数据)的更多相关文章
- C/C++数据在内存中的存储方式
目录 1 内存地址 2 内存空间 在学习C/C++编程语言时,免不了和内存打交道,在计算机中,我们存储有电影,文档,音乐等数据,这些数据在内存中是以什么形式存储的呢?下面做一下简单介绍. 本文是学 ...
- C语言之数据在内存中的存储
C语言之数据在内存中的存储 在我们学习此之前,我们先来回忆一下C语言中都有哪些数据类型呢? 首先我们来看看C语言中的基本的内置类型: char //字符数据类型 short //短整型 int //整 ...
- 使用程序获取整型数据和浮点型数据在内存中的表示---gyy整理
使用程序获取整型数据和浮点型数据在内存中的表示. C++中整型(int).短整型(short int).单精度浮点数(float).双精度浮点数(double)在内存中所占字节数不同,因此取值范围也不 ...
- Java的各类型数据在内存中分配情况详解
1. 有这样一种说法,如今争锋于IT战场的两大势力,MS一族偏重于底层实现,Java一族偏重于系统架构.说法根据无从考证,但从两大势力各自的社区力量和图书市场已有佳作不难看出,此说法不虚,但 ...
- LITTLE-ENDIAN(小字节序、低字节序) BOM——Byte Order Mark 字节序标记 数据在内存中的存放顺序
总结: 1. endian 字节存放次序 字节序,顾名思义字节的顺序,再多说两句就是大于一个字节类型的数据在内存中的存放顺序(一个字节的数据当然就无需谈顺序的问题了). 2. LITTLE-ENDIA ...
- Redis为什么要把所有数据放到内存中?
Redis为了达到最快的读写速度将数据都读到内存中,并通过异步的方式将数据写入磁盘.所以Redis具有快速和数据持久化的特性. 如果不将数据放到内存中,磁盘的I/O速度会严重影响redis的性能.在内 ...
- 为什么redis 需要把所有数据放到内存中?
答:Redis 为了达到最快的读写速度将数据都读到内存中,并通过异步的方式将数 据写入磁盘.所以 redis 具有快速和数据持久化的特征.如果不将数据放在内存中, 磁盘 I/O 速度为严重影响 red ...
- 为什么 redis 需要把所有数据放到内存中?
答Redis 为了达到最快的读写速度将数据都读到内存中,并通过异步的方式将数 据写入磁盘.所以 redis 具有快速和数据持久化的特征.如果不将数据放在内存中, 磁盘 I/O 速度为严重影响 redi ...
- SpringMvc 系统启动时加载数据到内存中
SpringMvc 系统启动时加载数据到内存中 学习了:http://blog.csdn.net/newstruts/article/details/18668269 https://www.cnbl ...
随机推荐
- 开源的.NET任务调度框架-HangFire
什么是Hangfire Hangfire 是一个开源的.NET任务调度框架,目前1.6+版本已支持.NET Core.内置提供集成化的控制台,方便后台查看及监控: 另外,Hangfire包含三大核心组 ...
- runtime MethodSwizzle 实践之扩展 NIAttributedLabel
runtime MethodeSwizzle 提供 简单的方法交换已知类的 Method IMP. Method 可以是 外部可访问的 public 或者 private Method .所谓的属性 ...
- Socket网络编程--简单Web服务器(4)
上一小节已经实现了对图片的传输,接下来就是判断文件是否为js,css,png等格式.我们增加一个函数用于判断格式 int WebServer::get_filetype(char *type,char ...
- 获取应用程序根目录物理路径(Web and Windows)
这两个计划写一个小类库,需要在不同项目下任意调用.该类库需要对磁盘文件进行读写,所以就需要获取程序执行的磁盘路径,就简单的对获取磁盘路径的方法进行研究. 借助搜索引擎,我从网上搜罗来多种方法,都可以直 ...
- linux每日命令(12):nl命令
nl命令在linux系统中用来计算文件中行号.nl 可以将输出的文件内容自动的加上行号!其默认的结果与 cat -n 有点不太一样, nl 可以将行号做比较多的显示设计,包括位数与是否自动补齐 0 等 ...
- c++命名空间---namespace
C++ 命名空间 C++ 应用程序中.例如,您可能会写一个名为 func() 的函数,在另一个可用的库中也存在一个相同的函数 func().这样,编译器就无法判断您所使用的是哪一个 func() 函数 ...
- elasticsearch和mysql排序问题
elasticsearch 字段类型错误 最近用elasticseach做排序,排序字段是float型的,没有使用mapping,是直接写代码导入的,没想到排序时如果有小数和整数就会出现错误. 于是查 ...
- 阿里云 FTP 无法读取目录问题
安全组除了添加制定 FTP端口外 需要加入再添加1024/65535端口 放行策略,ftp被动模式需要随机开一个此范围端口进行传输 添加安全组规则参考文档:https://help.aliyun.co ...
- MUI 打包android app
自有证书生成方法 manifest配置 (2).图标配置:所有图片格式必须是png,且严格符合分辨率要求.使用其他图片格式重命名为png会导致打包失败.配置图标时选择自动生成所有适用图标,选择一个大图 ...
- Python 函数 (关键字参数)
关键字参数 可变参数允许你传入0个或者任意个参数,这些可变参数在函数调用时会自动组装成一个tuple,而关键字参数允许你传入0个或者任意个含参数名的参数,这些参数在函数内部自动组装成为一个dict d ...