字节序、位序

字节序

    字节序,又称端序、尾序,英文单词为Endian,该单词来源于于乔纳森·斯威夫特的小说《格列佛游记》,小说中的小人国因为吃鸡蛋的问题而内战,战争开始是由于以下的原因:我们大家都认为,吃鸡蛋前,原始的方法是打破鸡蛋较大的一端。可是当今皇帝的祖父小时候吃鸡蛋,一次按古法打鸡蛋时碰巧将一个手指弄破了,因此他的父亲,当时的皇帝,就下了一道敕令,命令全体臣民吃鸡蛋时打破鸡蛋较小的一端,违令者重罚。老百姓们对这项命令极为反感。历史告诉我们,由此曾发生过六次叛乱,其中一个皇帝送了命,另一个丢了王位…关于这一争端,曾出版过几百本大部著作,不过大端派的书一直是受禁的,法律也规定该派的任何人不得做官。 1980年,Danny Cohen在其著名的论文"On Holy Wars and a Plea for Peace"中,为平息一场关于字节该以什么样的顺序传送的争论,而引用了该词。

         在计算机科学领域中,字节序是指存放多字节数据的字节(byte)的顺序,典型的情况是整数在内存中的存放方式和网络传输的传输顺序。有时候也可以用指位序(bit)。为了更好地理解,先看下面这段小程序,这个程序是把一个包含4位数字的字符串转换为16进制整数来存储,16进制整数的每一个字节存储一位数字字符。比如:”1234”,转换成16进制整数0x01020304。

程序1清单:

#include <stdio.h>

#include <conio.h>

 

int main( )

{

     char input[4] = {0};

     int integer   = 0;

     int i;

     printf("/r/n请输入一个位数,每一位的范围是从到0到9/r/n");

     for(i = 0; i < 4; i++)

     {

         input[i] = getch();

         if(input[i] > '9' || input[i] < '0')

         {

              printf("input error!/r/n");

              return 1;

         }

         putch(input[i]);

     }

    getch();

     putch('/n');

 

     for(i = 0; i < 4; i++)

     {

         input[i] = input[i] - '0';

     }

 

     memcpy((void*)&integer, (void*)input, 4);

 

     printf("转换后的进制数是:0x%08x/r/n", integer);

 

     getch();

 

     return 0;

}

 

现在来分析一下这段代码

首先,定义了一个字符数组input,用来接收用户输入的4个数字字符;

第二,把4个字符数字转换成对应的数字;

第三,把转换的数字复制到整型变量integer中;

最后在屏幕上打印。

如果在PPC或者ARM的机器上编译运行这个程序,那么会在屏幕上打印出结果:0x01020304,这与我们的预期一致;但是在X86的机器上则打印出的结果是:0x04030201。这个令人惊讶的结果正是字节序问题引起。下面来详细谈谈这个问题。

 

    从计算机诞生之后,就有几种不同的字节序,典型的是大端序(big endian)和小端序(little endian),当然还有不常见的混合序(middle endian)。这些用来都是描述多字节数据在内存中的存放方式的。以上面的16进制数0x01020304为例,在计算机中需要用4个字节来保存它,’01’, ’02’,’03’,’04’各占一个字节。按照人类的计数习惯,最左边的’01’,称之为最高有效位(MSB,Most Significant Byte,它具有最高权重);最右边的’04’称之为最低有效位(LSB, Least Significant Byte,他具有最低权重);在计算机中需要用4个字节来保存它,其中’01’, ’02’,’03’,’04’各占一个字节。大端序的计算机保存这个数值时,按照从低地址到高地址的顺序分别保存MSB到LSB四个字节,0x01020304的存储情况如下图所示:

 

而小端序的计算机则以相反的顺序来保存它,如下图所示:

 

 

可以看到,在小端序的计算机中,0x01020304的保存顺序恰好与上面的程序1中相反,这就是最后输出结果为0x04030201的原因;大端序的计算机中两者保存顺序一致,所以打印正确。

 

    我们可以在VC集成环境中来验证上面的分析。在VS2005中调试下面的小程序,在return语句处设置断点,断住后打开内存窗口查看&i处的内容。可以直观的看到在x86(小端序)的机器上整数的存放方式。

程序2清单:

#include <stdio.h>

int main()

{

     int i = 0x01020304;

     printf("i = %#x/r/n",i);

     return 0;

}

 

 

 

 

 

再看看如何才能让程序1在大端序和小端序的机器上都能正确执行呢?一个办法就是利用预编译宏,针对不同的机器定义定义不同的数据结构。下面是一个例子:

程序3清单:

#include <stdio.h>

#include <conio.h>

 

typedef union

{

     struct{

#ifdef BIG_ENDIAN

     char msb;

     char midb1;

     char midb2;

     char lsb;

#else

     char lsb;

     char midb2;

     char midb1;

     char msb;

#endif

     } bytes;

     int  var;

} INTEGER;

 

int main()

{

     int  i          = 0;

     char input[5]   = {0};

     INTEGER integer = {0};

 

     printf("/r/n请输入一个位数,每一位的范围是从到到/r/n");

     for(i = 0; i < 4; i++)

     {

         input[i] = getch();

         if(input[i] > '9' || input[i] < '0')

         {

              printf("input error!/r/n");

              return 1;

         }

         putch(input[i]);

     }

    getch();

     putch('/n');

 

     integer.bytes.msb   = input[0] - '0';

     integer.bytes.midb1 = input[1] - '0';

     integer.bytes.midb2 = input[2] - '0';

     integer.bytes.lsb   = input[3] - '0';

 

     printf("转换后的进制数是:0x%08x/r/n", integer.var);

 

     getch();

 

     return 0;

}

可以看到,这段代码定义了两套数据结构,通过BIG_ENDIAN这个宏定义来决定使用哪一套数据结构。这是个笨拙却有效的方法。下面这个例子则漂亮一些:

程序4清单

#include <stdio.h>

#include <conio.h>

#include <memory.h>

#include <winsock2.h>

 

int main( )

{

     char input[4] = {0};

     int integer   = 0;

     int i;

     printf("/r/n请输入一个位数,每一位的范围是从到到/r/n");

     for(i = 0; i < 4; i++)

     {

         input[i] = getch();

         if(input[i] > '9' || input[i] < '0')

         {

              printf("input error!/r/n");

              return 1;

         }

         putch(input[i]);

     }

    getch();

     putch('/n');

 

     for(i = 0; i < 4; i++)

     {

         input[i] = input[i] - '0';

     }

 

     memcpy((void*)&integer, (void*)input, 4);

 

     integer = ntohl(integer);

 

     printf("转换后的进制数是:0x%08x/r/n", integer);

 

     getch();

 

     return 0;

}

这个程序利用了大端序与人类书写习惯一致的特点,通过ntohl函数将整数进行转换。这个函数的功能是将网络序转换成主机序,在大端机器上它什么也不做,在小端机器上它会将输入参数的值转换成小端序的值。在windows环境下链接时别忘了将Ws2_32.lib库添加进来。

 

主机序和网络序

主机序就是指主机的端序。

网络字节序(网络序)指多字节数据在网络传输中的顺序,TCP协议规定网络序是大端序,即高字节先发送。因此大端序的机器接受到的数据可直接使用,小端序机器则需要转换后使用。BSD socket API中定义了一组转换函数,用于16和32bit整数在网络序和本机字节序之间的转换。htonl,htons用于本机序转换到网络序;ntohl,ntohs用于网络序转换到本机序。一般来说,为了保证程序的可移植性,编写代码时,发送的数据需要使用htonl、htons转换,接收到的数据要使用ntohl、ntohs转换。

注意:不存在对单字节整数进行转换的函数”ntohc”和”htonc”!

位序

         位序,一般用于描述串行设备的传输顺序。一般说来大部分硬件都是采用小端序(先传低位),因此,对于一个字节数据,大部分机器上收发的顺序都一样,不会有问题,这就是为什么没有针对单字节数据的API接口”ntohc”和”htonc”。当然,也有例外,比如­I2C协议就是采用了大端序。这些细节只有在网络协议的数据链路层底端才会碰到,对一般的程序员来说很少涉及。

         但是在C语言中存在一种特殊的数据结构:位域。它的存在,使得C程序员能方便地进行位操作(比如在网络协议中经常出现1bit或者多bit的标示位,它们不是一个完整的字节)。但同时也引起一些难以察觉的问题,这些问题的根源仍然是前面提到的端序。

 

         与字节序一样,一个字节中的8个bit顺序在不同端序的机器上并不相同。大端机器上从低地址到高地址顺寻分别是msb->lsb,如下图:

 

 

小端序的机器上则正好相反

 

现代计算机的最小存储单位是BYTE,无法对bit寻址,因此我们无法直接观察每个字节内部bit的顺序。但是我们仍然可以通过位域来间接观察字节内部bit顺序,以印证上面的说法。

在C语言中,位域与结构体类似,其语法规定:先声明的成员位于低地址,后声明的成员位于高地址。那么下面的位域中:

typedef struct OneByte

{

     bt0 : 1;

     bt1 : 1;

     bt2 : 1;

     bt3 : 1;

     bt4 : 1;

     bt5 : 1;

     bt6 : 1;

     bt7 : 1;

}

成员bt0就位于一个字节中最低地址bit0处,成员bt7就位于一个字节的最地址bit7处。

 

我们看看下面的程序。

#include <stdio.h>

 

typedef struct OneByte

{

     char bt0 : 1;

     char bt1 : 1;

     char bt2 : 1;

     char bt3 : 1;

     char bt4 : 1;

     char bt5 : 1;

     char bt6 : 1;

     char bt7 : 1;

} ONE_BYTE;

 

int main()

{

     ONE_BYTE onebyte = {0};

    

     onebyte.bt7 = 1;

 

     printf("onebyte = %#x/r/n", *((unsigned char *)&onebyte));

 

     return 0;

}

 

当bt7赋值为1后,onebyte在内存中是这个样子的:

 

而在VC2005中编译运行的结果如下:

 

0x80转换成二进制是1000 0000。由于在X86(小端序)中,高地址bit7是msb,因此onebyte的值是0x80了;这就证实了前面的说法。 相应的如果是在大端序计算机中,bit7是lsb,则onebyte的值是0x01。

 

 

 

未完待续

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/435898.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

怎么抓取屏幕截图计算机考试时间,定时抓屏快照(电脑截屏工具)V4.8 最新版

定时抓屏快照(电脑截屏工具)是一款非常实用的电脑屏幕定时抓拍软件。想定时截取电脑屏幕&#xff1f;定时抓屏快照(电脑截屏工具)轻松帮助用户。马上要放假了&#xff0c;家长一定很担心小孩最新的上网情况&#xff0c;看是在电脑上玩什么&#xff0c;用户通过该软件可以及时了…

什么是IIS

IIS的设计目的是建立一套集成的服务器服务&#xff0c;用以支持HTTP&#xff0c;FTP和SMTP&#xff0c;它能够提供快速且集成了现有产品&#xff0c;同时可扩展的Internet服务器。IIS相应性极高&#xff0c;同时系统资源的消耗也是最少&#xff0c;IIS的安装&#xff0c;管理和…

【转】C#检查键盘大小写锁定状态的方法

转自&#xff1a; 本文实例讲述了C#检查键盘大小写锁定状态的方法。分享给大家供大家参考。具体分析如下&#xff1a; 1、命名空间&#xff1a; 1 using System.Runtime.InteropServices; 2、导入方法 1 2 [DllImport("user32.dll", EntryPoint "GetKeybo…

计算机系统的备份与还原实验报告,数据库《数据备份与还原》实验报告.doc

《数据库《数据备份与还原》实验报告.doc》由会员分享&#xff0c;提供在线免费全文阅读可下载&#xff0c;此文档格式为doc&#xff0c;更多相关《数据库《数据备份与还原》实验报告.doc》文档请在天天文库搜索。1、&#xfeff;xx大学计算机与信息技术学院实验报告姓 名学 号…

jquery智能提示

中文提示文件http://code.google.com/p/csm-ui/downloads/list aspx页面编写时候<script src"http://www.cnblogs.com/Scripts/jquery-1.3.2.min.js" type"text/javascript"></script><script src"http://www.cnblogs.com/Scripts/jqu…

【转】matlab函数编译成dll供Cpp调用的方法

转自&#xff1a;https://www.cnblogs.com/vincenzo/archive/2009/09/09/1563467.html 以前做过matlab7与c的混合编程&#xff1a;将m函数编译成dll给C调用&#xff0c;从而加快开发的进度。但是今天在matlab2008b下面又做了一遍&#xff0c;发现matlab又改了很多东西&#xf…

可以测试流放之路伤害的软件,测试平台及细节一览 - 《流放之路》国服硬件需求测试:低配也能续写ARPG传奇 - 超能网...

测试平台、场景说明测试平台测试平台配置主要分为两套&#xff0c;第一套配置为Skylake平台基础搭建的ASUS MAXIMUS VIIIGEN(Z170)主板&#xff0c;处理器为Intel Core i7-6700K&#xff0c;内存是G.Skill Ripjaws V 4GB DDR43200x2组成的双通道&#xff1b;第二套配置为Kaveri…

关于增强(五)-Class Enhancement

Class/Interface增强允许增加&#xff0c; 对已有的方法增加可选参数 添加方法&#xff0c;事件&#xff0c;事件处理 参考接口 对存在的方法添加出口&#xff0c;其中包括方法开始前的出口&#xff08;Pre-Exit&#xff09;&#xff0c;方法结束快结束的出口(Post-Exit)&#…

VxWorks基本OS介绍

这里我就偷点懒&#xff0c;直接复制粘贴文档了。可能会有一些图片无法显示 &#xff0c;可以直接管我索要文档。 每一个被切换的程序实体就是“任务”。每一个任务都有其自己的上下文——所谓上下文就是指任务在每一次被核心调度时所需要的CPU环境、系统资源。在上下文的切换…

【转】彻底理解cookie,session,token

转自&#xff1a;https://zhuanlan.zhihu.com/p/63061864 彻底理解cookie&#xff0c;session&#xff0c;token 发展史 1、很久很久以前&#xff0c;Web 基本上就是文档的浏览而已&#xff0c; 既然是浏览&#xff0c;作为服务器&#xff0c; 不需要记录谁在某一段时间里都浏…

四年级信息技术认识计算机,四年级信息技术第三课信息工具知多少

一、教学目标&#xff1a;1、了解信息工具有哪些&#xff1b;2、认识电脑的组成&#xff1b;了解计算机硬件和软件的概念&#xff1b;3、学习开机、关机的步骤。4、建立对计算机的感性认识&#xff0c;培养学生学习、使用计算机的兴趣。二、教学重点&#xff1a;知道电脑是信息…

坐标系统概念

作者:江帆 3推荐一&#xff1a;需要用到的几个基本概念-------- 球面坐标系 1. 几个常涉及到的名词的中英文对照&#xff1a;地形面&#xff08;Topography&#xff09;&#xff1b;大地水准面&#xff08;Geoid&#xff09;&#xff1b;参考椭球面&#xff08;Referen…

VxWorks概念

VxWorks操作系统的集成环境叫Tornado。Tornado集成环境提供了高效明晰的图形化的实时应用开发平台&#xff0c;它包括一套完整的面向嵌入式系统的开发和调测工具。Tornado环境采用主机&#xff0d;目标机交*开发模型&#xff0c;应用程序在主机的Windows环境下编译链接生成可执…

【转】c#数字图像处理(一)Bitmap类、 Bitmapdata类和 Graphics类

转自&#xff1a;https://www.cnblogs.com/dearzhoubi/p/8553763.html Bitmap类、 Bitmapdata类和 Graphics类是C#图像处理中最重要的3个类,如果要用C# 进行图像处理,就一定要掌握它们。 1.1 Bitmap类 Bitmap对象封装了GDI中的一个位图,此位图由图形图像及其属性的像素数据组成…

单片机与PC机一样都是计算机,51单片机与PC机通信资料

《51单片机与PC机通信资料》由会员分享&#xff0c;可在线阅读&#xff0c;更多相关《51单片机与PC机通信资料(14页珍藏版)》请在人人文库网上搜索。1、专业综合实习报告专 业&#xff1a; 电子信息工程 年 级&#xff1a; 2013级 指导教师&#xff1a; 学 生&#xff1a; 目录…

VB6:通过ADO访问Oracle存储过程返回的结果集

微软的MDAC中oledb for oracle 驱动——对过程参数&#xff1a;估计&#xff1a;位置绑定 该驱动最新支持到Oracle8i的水平&#xff0c;且不再更新了使用该驱动。只能使用ODBC的存储过程调用方式&#xff1a; "{call procename (?,?,....)}"只能支持返回一个记录集…

【转】c#数字图像处理(二)彩色图像灰度化,灰度图像二值化

转自&#xff1a;https://www.cnblogs.com/dearzhoubi/p/8571652.html 为加快处理速度,在图像处理算法中,往往需要把彩色图像转换为灰度图像,在灰度图像上得到验证的算法,很容易移 植到彩色图像上。 24位彩色图像每个像素用3个字节表示,每个字节对应着R、G、B分量的亮度(红、绿…

400呼叫中心服务器,400电话呼叫中心服务新模式

呼叫中心模式也很多种&#xff0c;但是以400电话呼叫中心的新服务模式受到了各行各业的关注。现在企业为了提升自己的形象以及办事效率&#xff0c;纷纷办理400电话&#xff0c;对于400电话呼叫中心这一模式也开始积极采用。采用400电话呼叫中心模式也是有其道理的&#xff0c;…

ASP.NET开发安全问题

ASP.NET开发安全问题 前言&#xff1a;之所以想到要写ASP.NET安全问题的文章&#xff0c;是有感于自己的一些经历。想当初自己学习ASP.NET的时候&#xff0c;到处找有关安全方面的文章&#xff0c;当时发现很多文章指示蜻蜓点水的告诉我怎么用&#xff0c;如安全的主要问题有哪…

互斥信号量和二进制信号量

互斥信号量和二进制信号量的区别 互斥型信号量必须是同一个任务申请&#xff0c;同一个任务释放&#xff0c;其他任务释放无效。同一个任务可以递归申请。 二进制信号量&#xff0c;一个任务申请成功后&#xff0c;可以由另一个任务释放。 二进制信号量实现任务互斥&am…