Big endian and Little endian_iteye_20025的博客-程序员ITS301

技术标签: java  网络  swift  

谈到字节排序的问题,必然牵涉到两大CPU派系。那就是Motorola的PowerPC系列CPU和Intel的x86系列
CPU。PowerPC系列采用big endian方式存储数据,而x86系列则采用little
endian方式存储数据。ARM同时支持 big和little,实际应用中通常使用little
endian。那么究竟什么是big endian,什么又是little endian呢?

其实big endian是指低地址存放最高有效字节(MSB),而little
endian则是低地址存放最低有效字节(LSB)。用文字说明可能比较抽象,下面用图像加以说明。比如数字0x12345678在两种不同字节序CPU中的存储顺序如下所示:

Big Endian
一个Word中的高位的Byte放在内存中这个Word区域的低地址处

低地址 高地址
----------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 12 | 34 | 56 | 78 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Little Endian
一个Word中的低位的Byte放在内存中这个Word区域的低地址处

低地址 高地址
----------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 78 | 56 | 34 | 12 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

从上面两图可以看出,采用big
endian方式存储数据是符合我们人类的思维习惯的。必须注意的是:一个Word的长度是16位,一个Byte的长度是8位。如果一个数超过一个
Word的长度,必须先按Word分成若干部分,然后每一部分(即每个Word内部)按Big-Endian或者Little-Endian的不同操作来
处理字节。
一个例子:
如果我们将0x1234abcd写入到以0x0000开始的内存中,则结果为
big-endian little-endian
0x0000 0x12 0xcd
0x0001 0x34 0xab
0x0002 0xab 0x34
0x0003 0xcd 0x12

需要特别说明的是,以上假设机器是每个内存单元以8位即一个字节为单位的.简单的说,little
endian把低位存放到高位,而big endian把低位存放到低位.
现在主流的CPU,intel系列的是采用的little
endian的格式存放数据,而motorola系列的CPU采用的是big endian.
以下是判断字节存储顺序的可移植的C语言代码:
/*可移植的用于判断存储格式是
little endian还是big ednian的C代码*/
#include < stdio.h >
union
{
long Long;
char Char[ sizeof ( long )];
} u;

int main()
{
u.Long = 1 ;
if (u.Char[ 0 ] == 1 )
{
printf( " Little Endian!\n " );
}
else if (u.Char[ sizeof ( long ) - 1 ] == 1 )
{
printf( " Big Endian!\n " );
}
else
{
printf( " Unknown Addressing!\n " );
}

printf( " Now, Let's look at every byte in the memory!\n " );
for ( int i = 0 ; i < sizeof ( long ); ++ i)
{
printf( " [%x] = %x\n " , & u.Char[i], u.Char[i]);
}
return 0 ;
}
在 网络编程中,TCP/IP统一采用big
endian方式传送数据,也就是说,假设现在是在一个字节顺序是little
endian的机器上传送数据,要求传送的数据是0XCEFABOBO,那么你就要以0XBOBOFACE的顺序在unsigned
int中存放这个数据,只有这样才能保证存放的顺序满足TCP/IP的字节顺序要求.很多时候,需要自己编写应用层的协议,字节顺序的概念在这个时候就显
得及其的重要了.
下面给出的是在big endian和little
endian中相互转换的代码,C语言强大的位操作的能力在这里显示了出来:
/*在little endian和big ednian之间相互转化数据的演示代码*/
#include < stdio.h >
const unsigned char SIZE_OF_UNSIGNEDINT = sizeof (unsigned int );
const unsigned char SIZE_OF_UNSIGNEDCHAR = sizeof (unsigned char );

void put_32(unsigned char * cmd, unsigned int data)
{
int i;
for (i = SIZE_OF_UNSIGNEDINT - 1 ; i >= 0 ; -- i)
{
cmd[i] = data % 256 ;
// 或者可以:
// cmd[i] = data & 0xFF;
data = data >> 8 ;
}
}

unsigned int get_32(unsigned char * cmd)
{
unsigned int ret;
int i;
for (ret = 0 , i = SIZE_OF_UNSIGNEDINT - 1 ; i >= 0 ; -- i)
{
ret = ret << 8 ;
ret |= cmd[i];
}
return ret;
}

int main( void )
{
unsigned char cmd[SIZE_OF_UNSIGNEDINT];
unsigned int data, ret;
unsigned char * p;
int i;
data = 0x12345678 ;
printf( " data = %x\n " , data);
// 以字节为单位打印出数据
p = (unsigned char * )( & data);
for(i = 0 ; i < SIZE_OF_UNSIGNEDINT; ++ i)
{
printf( " %x " , * p ++ );
}
printf( " \n " );
//以相反的顺序存放到cmd之中
put_32(cmd, data);
for (i = 0 ; i < SIZE_OF_UNSIGNEDINT; ++ i)
{
printf( " cmd[%d] = %x\n " , i, cmd[i]);
}
// 再以相反的顺序保存数据到ret中
// 保存之后的ret数值应该与data相同
ret = get_32(cmd);
printf( " ret = %x\n " , ret);
p = (unsigned char * )( & ret);
for(i = 0 ; i < SIZE_OF_UNSIGNEDINT; ++ i)
{
printf( " %x " , * p ++ );
}
printf( " \n " );
return 0 ;
}

为什么要注意字节序的问题呢?你可能这么问。当然,如果你写的程序只在单机环境下面运行,并且不和别人的程序打交道,那么你完全可以忽略字节序的存在。但
是,如果你的程序要跟别人的程序产生交互呢?在这里我想说说两种语言。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中提供了下面四个转换字节序的宏。

当前的存储器,多以byte为访问的最小单元,当一个逻辑上的整理必须分割为物理
上的若干单元时就存在了先放谁后放谁的问题,于是endian的问题应运而生了,对于不同的存储方法,就有Big-endian和Little-
endian两个描述.(这两个术语来自于 Jonathan Swift
的《《格利佛游记》其中交战的两个派别无法就应该从哪一端--小端还是大端--打开一个半熟的鸡蛋达成一致。在那个时代,Swift是在讽刺英国和法国之
间的持续冲突,Danny
Cohen,一位网络协议的早期开创者,第一次使用这两个术语来指代字节顺序,后来这个术语被广泛接纳了。)
存在"如果说"跟word或者说字长根本就没关系",假设有一数据文件里面有N多数顺序排布,如果想以Little-Endian
format
读入内存某区域,那么应该怎么读?怎么排?"这样的问题是由于对于endian的实质理解的偏差,endian指的是当物理上的最小单元比逻辑上的最小单
元小时,逻辑到物理的单元排布关系。这里的"有一数据文件里面有N多数顺序排布",这个"有一数据"显然不是逻辑上的最小单元,而其中的"N多数"的一个
才是逻辑最小单元,于是可应用楼主表格中的原则排列,而"N多数"之间的顺序则是由这"N多数"的宿主决定的,比如是你写的程序,这个顺序由你决定.

刚才谈到了,endian指的是当物理上的最小单元比逻辑上的最小单元小时,逻辑到物理的单元排布关系。咱们接触到的物理单元最小都是byte,在通信领域中,这里往往是bit,不过原理也是类似的。

实践可以给你更多的经验,比如在一个嵌入式系统的通信协议中,从底层射频驱动到上层的协议栈全部需要实现,那么很可能遇到多个endian的问题,底层的bit序、协议层的byte序、应用层的byte序,这些都是不同的概念.

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/iteye_20025/article/details/82197588

智能推荐

convert oracle 字符串_Oracle Convert()函数_weixin_39521009的博客-程序员ITS301

在 Oracle 中,Convert() 函数可以将字符串从一个字符集转换为另一个字符集。本文要为大家带来的就是 Convert() 函数的使用方法。句法Convert() 函数语法CONVERT( string1, char_set_to [, char_set_from] )string1:要转换的字符串。char_set_to:要转换为的字符集。char_set_from:可选的,要从中转换...

linux 安装actrive mq,ActiveMQ学习之jdbc消息持久化_孤独的根号 三的博客-程序员ITS301

1、将mysql的驱动包复制到activemq的lib目录下将mysql驱动包(驱动名称:mysql-connector-java-5.1.46.jar)复制到activemq的lib文件夹内2、配置persistenceAdapter的jdbc进入到安装目录根目录下conf文件夹中,打开activemq.xml,修改成jdbc的持久化修改之前: 修改后: 3、数据库连接池配置将下面配置到acti...

systemd设置nginx开机自启动_运维猫(运维开发)的博客-程序员ITS301

1、简介服务器每次重启,都需要手动启动一些服务,这不是一个程序员可以忍受的,难怪大家都喜欢写脚本。CentOS7之后已不再使用chkconfig管理启动项,而是使用systemd。Lin...

单目摄像头与激光雷达的信息融合_Mr.Naruto的博客-程序员ITS301_单目与雷达融合

本科毕设,我做的就是这个摄像头和雷达信息的融合。正当我愁怎么下手呢,恰好我看到一篇论文,讲的就是这个方面已取得的成绩,下面我主要分享看这篇论文的感受。本人英文水平,学术能力有限,如有错误,欢迎留言。                     LIMO:Lidar-Monocular Visual Odometry首先分析了摄像头的缺点。摄像头:在进行特征点的捕捉时,可能会有误差,如...

php和java做web的区别_PHP与JAVA做WEB开发的一些区别_美自的博客-程序员ITS301

一.SESSION使用方法的区别PHP中的SESSION的使用方法:1.每次使用SESSION必须使用SESSION_START();2.注册SESSION使用SESSION_REGISTER();3.SESSION初始化使用$_SESSION["SESSION名"];4.SESSION使用完后须使用UNSET()或SESSION_UNREGISTER()方法关闭SESSION.(其中第2,4点在...

随便推点

c0000005 Access Violation_Vinx911的博客-程序员ITS301_c0000005 access_violation

出现这种错误原因总结:1. 指针异常。引用指针前判断指针是否为空2. 数据越界。一个典型例子,char ValueName[256];strncpy(ValueName,&amp;amp;value-&amp;gt;Name,value-&amp;gt;NameLength);value-&amp;gt;NameLength 大于了256,致使ValueName访问越界,崩溃顺便记录崩溃调试的方法...

第2章 - 程序的基本结构_2112222222222的博客-程序员ITS301

2.1 - 2.4 启动第一个flask服务器from flask import Flaskapp = Flask(__name__)#申请一个处理客户端请求的对象@app.route('/')#路由:处理url请求和python函数的映射关系,客户端请求的url('/')会相应的在这部分处理def index(): #视图函数 ,返回值是响应,也就是客户端收到的内容...

Plato Farm通过LaaS协议Elephant Swap,为社区用户带来全新体验_大个子音乐家的博客-程序员ITS301

将成为刚需,因为大家都需要ePLATO来进一步解锁ePLATO.而在ElephantSwap。资产,我们需要在ElephantSwap中进行交易行为来解锁ePLATO.ePLATO。投资回报,据悉在PLATO代coin上线ElephantSwap后,将能够为投资。在PLATO上线ElephantSwap后,任何持有PLATO。而在PLATO上线ElephantSwap后,ePLATO。比如PLATO,需要将其在ElephantSwap中进行。...

ctrip-apollo多环境部署-史上最简单_cf的博客-程序员ITS301

一套Portal可以管理多个环境,但是每个环境都需要独立部署一套Config Service、Admin Service和ApolloConfigDB,apollo 0.10.2版本默认支持的环境为:LOCAL, DEV, FWS, FAT, UAT, LPT, PRO, TOOLS(ps:环境枚举类, com.ctrip.framework.apollo.core.enums.Env, 添加自...

ansible支持python版本_【Ansible】的python api_weixin_39907939的博客-程序员ITS301

【Ansible API】Ansible本身就是由python写成,所有其对python形式的API的支持应该不错。其API分不同的版本,这个版本也就是ansible本身的版本,可以通过ansible --version命令查看或者在python中import ansible然后查看anisble.__version__。在2.0的版本以前,ansible的API十分简单。通过大概十几行代码就可以...

c语言中各种函数的作用,C语言常用函数用法大全_Bay Bay的博客-程序员ITS301

C语言是当中广泛的计算机编程语言,是所有计算机编程语言的祖先,其他计算机编程语言包括当前流行的Java语言,都是用C语言实现的,C语言是编程效率高的计算机语言,既能完成上层应用开发,也能完成底层硬件驱动编程,在计算机程序设计当中,特别是在底层硬件驱动开发当中,具有不可替代的作用。在C语言学习过程当中,除了学习好C语言基本语法外,掌握常用的C语言库函数的使用也是非常重要的,本文主要给同学们详细讲解在...

推荐文章

热门文章

相关标签