大端(Big Endian)与小端(Little Endian)详解
- 格式:doc
- 大小:57.00 KB
- 文档页数:14
⼤端模式与⼩端模式理解字节序字节序指多字节数据在计算机内存储或者⽹络上传输时各字节的顺序。
(来源:百度百科)为了⽅便,逻辑上将字节序列⾥左边的字节称为⾼字节,右边的字节称为低字节,从左到右,由⾼到低,这样符合数学上的思维习惯,左边是⾼位,右边是地位。
⼤端模式与⼩端模式由于每个字节在内存中都是有地址的,并且内存的地址是顺序排列的,当我们在内存中保存数据时:如果,⾼字节存放在低地址,低字节存放在⾼地址,则为⼤端模式(big-endian)。
如果,低字节存放在低地址,⾼字节存放在⾼地址,则为⼩端模式(little-endian)。
数据从内存保存到⽂件(或发送到⽹络上)时,会受到内存的⼤端模式与⼩端模式的影响。
数据从⽂件读取到(或从⽹络接收到)内存时,需要知道之前是先保存的(或是先发送的)⾼字节还是低字节。
C++⽰例代码1//int 占 4 个字节,short 占 2 个字节int main(){printf("在栈上分配内存\n");int a = 0x11223344;short b = 0x5566;short c = 0x7788;unsigned char *pa = (unsigned char *)&a;unsigned char *pb = (unsigned char *)&b;unsigned char *pc = (unsigned char *)&c;printf("pa 0x%p 0x%x\n", pa, a);printf("pb 0x%p 0x%x\n", pb, b);printf("pc 0x%p 0x%x\n", pc, c);printf("按字节序打印所有字节(⾼字节->低字节)\n");printf("a0 0x%x\n", (a & 0xFF000000) >> (3 * 8));printf("a1 0x%x\n", (a & 0x00FF0000) >> (2 * 8));printf("a2 0x%x\n", (a & 0x0000FF00) >> (1 * 8));printf("a3 0x%x\n", (a & 0x000000FF));printf("b0 0x%x\n", (b & 0xFF00) >> (1 * 8));printf("b1 0x%x\n", (b & 0x00FF));printf("c0 0x%x\n", (c & 0xFF00) >> (1 * 8));printf("c1 0x%x\n", (c & 0x00FF));printf("根据地址顺序打印所有字节(低地址->⾼地址)\n");for (int i = 0; i < 4; i++) {printf("pa[%d] 0x%p 0x%02x\n", i, pa + i, pa[i]);}for (int i = 0; i < 2; i++) {printf("pb[%d] 0x%p 0x%02x\n", i, pb + i, pb[i]);}for (int i = 0; i < 2; i++) {printf("pc[%d] 0x%p 0x%02x\n", i, pc + i, pc[i]);}return 0;}⽰例代码1运⾏结果在栈上分配内存pa 0x007ffe24 0x11223344pb 0x007ffe22 0x5566pc 0x007ffe20 0x7788按字节序打印所有字节(⾼字节->低字节)a0 0x11a1 0x22a2 0x33a3 0x44b0 0x55b1 0x66c0 0x77c1 0x88根据地址顺序打印所有字节(低地址->⾼地址)pa[0] 0x007ffe24 0x44pa[1] 0x007ffe25 0x33pa[2] 0x007ffe26 0x22pb[0] 0x007ffe22 0x66pb[1] 0x007ffe23 0x55pc[0] 0x007ffe20 0x88pc[1] 0x007ffe21 0x77⽰例代码1结果分析a、b、c 在内存中的排列情况:---------------------------------------------------|低地址 -> ⾼地址|---------------------------------------------------|....|0x88|0x77|0x66|0x55|0x44|0x33|0x22|0x11|....|---------------------------------------------------a、b、c 是在栈中分配的,可以看到内存地址是连续的,且 a 的地址相对较⾼,c 的地址相对较低。
在C语言中,结构体(struct)是一种自定义的数据类型,可以包含多个不同类型的数据成员。
位域(bit-field)是结构体中的一个特殊成员,用于存储固定位数的数据。
位域通常用于紧凑的数据存储,例如在嵌入式系统或低级编程中。
大小端(Endian)是指数据在内存中的存储顺序,分为大端(Big Endian)和小端(Little Endian)两种。
大端模式是指高位字节存储在内存的低地址处,而小端模式是指低位字节存储在内存的低地址处。
在结构体中定义位域时,需要指定每个位域的宽度和顺序,以便确定它们在内存中的布局。
同时,结构体的整体大小也会受到位域的影响。
由于位域是按照字节对齐的,因此如果位域的总宽度不是8的倍数,则会浪费一些空间。
关于大小端问题,对于结构体中的位域,其存储顺序与整型数据相同,即采用系统默认的大小端顺序。
因此,如果需要在不同大小端系统之间移植程序,需要注意位域的存储顺序是否一致。
如果需要确保位域的存储顺序一致,可以使用C标准库中的`#pragma pack`指令来指定结构体的对齐方式。
下面是一个简单的示例代码,演示了如何在C语言中使用结构体和位域:```c#include <stdio.h>struct MyStruct {char a; // 占用1个字节int b : 10; // 占用10个比特(1个字节)int c : 12; // 占用12个比特(1个字节)char d; // 占用1个字节};int main() {struct MyStruct s = {0x0A, 0x3F, 0x4B};printf("a: %x, b: %x, c: %x, d: %x\n", s.a, s.b, s.c, s.d);return 0;}```输出结果将显示`a`, `b`, `c`, `d`的值。
注意,由于系统默认采用大端模式,因此`b`和`c`的值将按照高位字节在前的方式存储和显示。
大端小端(Big-Endian和Little-Endian)字节序(Endian),大端(Big-Endian),小端(Little-Endian)图文并茂在各种计算机体系结构中,对于字节、字等的存储机制有所不同,因而引发了计算机通信领域中一个很重要的问题,即通信双方交流的信息单元(比特、字节、字、双字等等)应该以什么样的顺序进行传送。
如果不达成一致的规则,通信双方将无法进行正确的编/译码从而导致通信失败。
目前在各种体系的计算机中通常采用的字节存储机制主要有两种:Big-Endian和Little-Endian,下面先从字节序说起。
一、什么是字节序字节序,顾名思义字节的顺序,再多说两句就是大于一个字节类型的数据在内存中的存放顺序(一个字节的数据当然就无需谈顺序的问题了)。
其实大部分人在实际的开发中都很少会直接和字节序打交道。
唯有在跨平台以及网络程序中字节序才是一个应该被考虑的问题。
在所有的介绍字节序的文章中都会提到字节序分为两类:Big-Endian和Little-Endian,引用标准的Big-Endian和Little-Endian的定义如下:a) Little-Endian就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。
b) Big-Endian就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。
c) 网络字节序:TCP/IP各层协议将字节序定义为Big-Endian,因此TCP/IP协议中使用的字节序通常称之为网络字节序。
1.1 什么是高/低地址端首先我们要知道我们C程序映像中内存的空间布局情况:在《C专家编程》中或者《Unix环境高级编程》中有关于内存空间布局情况的说明,大致如下图:----------------------- 最高内存地址 0x ffffffff栈底栈栈顶-----------------------NULL (空洞)-----------------------堆-----------------------未初始化的数据----------------------- 统称数据段初始化的数据-----------------------正文段(代码段)----------------------- 最低内存地址 0x00000000以上图为例如果我们在栈上分配一个unsigned char buf[4],那么这个数组变量在栈上是如何布局的呢?看下图:栈底(高地址)----------buf[3]buf[2]buf[1]buf[0]----------栈顶(低地址)1.2 什么是高/低字节现在我们弄清了高/低地址,接着考虑高/低字节。
大端 (Big Endian与小端 (Little Endian简介Byte Endian 是指字节在内存中的组织 , 所以也称它为 Byte Ordering ,或 Byte Order。
对于数据中跨越多个字节的对象 , 我们必须为它建立这样的约定 :(1 它的地址是多少 ?(2 它的字节在内存中是如何组织的 ?针对第一个问题,有这样的解释 :对于跨越多个字节的对象,一般它所占的字节都是连续的, 它的地址等于它所占字节最低地址。
(链表可能是个例外, 但链表的地址可看作链表头的地址。
比如 : int x, 它的地址为 0x100。
那么它占据了内存中的 Ox100, 0x101, 0x102, 0x103这四个字节(32位系统,所以 int 占用 4个字节。
上面只是内存字节组织的一种情况 : 多字节对象在内存中的组织有一般有两种约定。
考虑一个 W 位的整数。
它的各位表达如下 :[Xw-1, Xw-2, ... , X1, X0],它的 MSB (Most Significant Byte, 最高有效字节为 [Xw-1, Xw-2, ... Xw-8];LSB (Least Significant Byte , 最低有效字节为 [X7, X6, ... , X0]。
其余的字节位于 MSB , LSB之间。
LSB 和 MSB 谁位于内存的最低地址, 即谁代表该对象的地址 ? 这就引出了大端 (Big Endian与小端 (Little Endian的问题。
如果 LSB 在 MSB 前面, 既 LSB 是低地址, 则该机器是小端 ; 反之则是大端。
DEC (Digital Equipment Corporation,现在是 Compaq 公司的一部分和 Intel 的机器(X86平台一般采用小端。
IBM , Motorola(Power PC, Sun的机器一般采用大端。
当然,这不代表所有情况。
有的 CPU 即能工作于小端, 又能工作于大端, 比如ARM , Alpha,摩托罗拉的 PowerPC 。
高位优先大端模式和(Big Endian)与地位优先小端模式(Little Endian)简介Byte Endian是指字节在内存中的组织,所以也称它为Byte Ordering,Byte Order。
对于数据中跨越多个字节的对象,我们必须为它建立这样的约定:(1) 它的地址是多少?(2) 它的字节在内存中是如何组织的?针对第一个问题,有这样的解释:对于跨越多个字节的对象,一般它所占的字节都是连续的,它的地址等于它所占字节最低地址。
(链表可能是个例外,但链表的地址可看作链表头的地址)。
比如: int x,它的地址为0x100。
那么它占据了内存中的Ox100,0x101, 0x102, 0x103这四个字节(32位系统,所以int占用4个字节)。
上面只是内存字节组织的一种情况: 多字节对象在内存中的组织有一般有两种约定。
考虑一个W位的整数。
它的各位表达如下:[Xw-1, Xw-2, ... , X1, X0],它的MSB (Most Significant Byte,最高有效字节)为 [Xw-1, Xw-2, ... Xw-8];LSB (Least Significant Byte,最低有效字节)为 [X7,X6,..., X0]。
其余的字节位于MSB, LSB之间。
LSB和MSB谁位于内存的最低地址,即谁代表该对象的地址?PS:内存的最低位置即对象的地址。
这个跟对战的压栈和出栈有关系。
相当于是栈顶指针。
这就引出了大端(Big Endian)与小端(Little Endian)的问题。
如果LSB在MSB前面,既LSB是低地址,则该机器是小端; 反之则是大端。
DEC (Digital Equipment Corporation,现在是Compaq公司的一部分)和Intel 的机器(X86平台)一般采用小端。
IBM, Motorola(Power PC), Sun的机器一般采用大端。
当然,这不代表所有情况。
⼤⼩端Big-Endian与Little-Endian应该说没做底层开发(硬件或驱动)的⼈很可能不会彻底理解⼤⼩端的概念,⼤⼩端不是简单的⼀句“⼤端在前”还是“⼩端在前”能够概括的问题。
在cpu, 内存, 操作系统, 编译选项, ⽂件,⽹络传输中均有⼤⼩端的概念,这些东西加在⼀起,就很容易把⼈搞晕。
我⾃⼰就晕过很久。
为⽅便说明,再做⼀些定义:(1) 内存可以存储若⼲个单元数据的物理设备,每个单元存储1个字节,每个单元有⼀个地址,其地址线程增长。
为⽅便说明,假设内存地址从0000:0000 ⼀直增加到FFFF:FFFF。
⽤⼀个带箭头的直线表⽰地址的增长⽅向,例如-------------------------->表⽰左边的数据处于低地址,右边的数据处于⾼地址。
(2) U32整型对于unsigned int的变量,计算机是以32bits存储的,即连续的4个字节。
⽐如说,对⼀个值为0x11223344的整数,在内存中的排列⽅式可能为:-------------------------->11 22 33 44 (⼤端)也可能为44 33 22 11 (⼩端)从CPU说起在有了上述定义之后,开始讲⼤⼩端的起源。
这得从CPU说起,我们知道CPU要从内存中加载程序数据来运⾏,CPU要对数字进⾏运算。
那么,CPU在从内存加载4个字节的数据之后,要把它作为⼀个数字来运算。
那它是怎么看待这个数字的呢?-------------------------->11 22 33 44有的CPU认为它是0x11223344,有的CPU认为它是0x44332211。
所以CPU就分为两类:Big-Endian和Little-Endian, 认为它是0x11223344的就是Big-Endian,认为它是0x44332211的就是Little-Endian。
有⼈会为,CPU的“认为”是什么意思。
这其实物理上的电路问题,CPU的所有运算都是通过电路完成,其连接逻辑已经决定了它是按⼤端运算还是按⼩端运算。
第一部分基本概念首先不管是大端法还是小端法存储,计算机在内存中存放数据的顺序都是从低地址到高地址,所不同的是首先取低字节的数据存放在低地址还是取高字节数据存放在低地址。
若首先取高字节的数据存放在低地址,则是大端法;若首先取低字节的数据存放在低地址,则是小端法;例如有数据0x (注意:左边是高字节,右边是低字节)大端法存放:存放的地址依次为:0x0001 0x0002 0x0003 0x0004对应的数据依次为:0x010x230x450x67示意图如下:小端法存放:存放的地址依次为:0x0001 0x0002 0x0003 0x0004对应的数据依次为:0x670x450x230x01示意图如下:第二部分判断一个机器是大端存放还是小端法存放知道了大端和小端的定义,我们很容易写代码判断一个机器是大端还是小端:int main(int argc, char* argv[]){union w{int a;char b;}c;c.a = 0x0001;if(c.b==1)printf("small endian\n");else if(c.b==0)printf("big endian\n");elseprintf("error\n");return 0;}注意:之所以可以这样写来判断是因为共用体中所有成员的存放顺序是从低地址开始的用这个程序可以判断intel PC机用的是小端法。
第三部分总结因为我们的思维习惯是从左到右的顺序,所以大端法的存储方式符号我们思维习惯,这因为这个原因,大端法也称为网络编码。
IBM的370主机,多数基于RISC计算机,和Motorola的微处理器使用big endian方法。
TCP/IP也使用big endian方法(big endian方法也叫做网络编码)。
因为我们的语言都是从左到右的习惯方式,所以我们希望存储字符和数字也以同样的方式出现。
⼤端和⼩端(BigendianandLittleendian)⼀、⼤端和⼩端的问题对于整型、长整型等数据类型,Big endian 认为第⼀个字节是最⾼位字节(按照从低地址到⾼地址的顺序存放数据的⾼位字节到低位字节);⽽ Little endian 则相反,它认为第⼀个字节是最低位字节(按照从低地址到⾼地址的顺序存放据的低位字节到⾼位字节)。
例如,假设从内存地址 0x0000 开始有以下数据:0x0000 0x0001 0x0002 0x00030x12 0x34 0xab 0xcd如果我们去读取⼀个地址为 0x0000 的四个字节变量,若字节序为big-endian,则读出结果为0x1234abcd;若字节序为little-endian,则读出结果为0xcdab3412。
如果我们将0x1234abcd 写⼊到以 0x0000 开始的内存中,则Little endian 和 Big endian 模式的存放结果如下:地址 0x0000 0x0001 0x0002 0x0003big-endian 0x12 0x34 0xab 0xcdlittle-endian 0xcd 0xab 0x34 0x12⼀般来说,x86 系列 CPU 都是 little-endian 的字节序,PowerPC 通常是 big-endian,⽹络字节顺序也是 big-endian还有的CPU 能通过跳线来设置 CPU ⼯作于 Little endian 还是 Big endian 模式。
对于0x12345678的存储:⼩端模式:(从低字节到⾼字节)地位地址 0x78 0x56 0x34 0x12 ⾼位地址⼤端模式:(从⾼字节到低字节)地位地址 0x12 0x34 0x56 0x78 ⾼位地址⼆、⼤端⼩端转换⽅法htonl() htons() 从主机字节顺序转换成⽹络字节顺序ntohl() ntohs() 从⽹络字节顺序转换为主机字节顺序Big-Endian转换成Little-Endian#define BigtoLittle16(A) ((((uint16)(A) & 0xff00) >> 8) | (((uint16)(A) & 0x00ff) << 8))#define BigtoLittle32(A) ((((uint32)(A) & 0xff000000) >> 24) | (((uint32)(A) & 0x00ff0000) >> 8) | \(((uint32)(A) & 0x0000ff00) << 8) | (((uint32)(A) & 0x000000ff) << 24))三、⼤端⼩端检测⽅法如何检查处理器是big-endian还是little-endian?C程序:int i = 1;char *p = (char *)&i;if(*p == 1)printf("Little Endian");elseprintf("Big Endian");⼤⼩端存储问题,如果⼩端⽅式中(i占⾄少两个字节的长度)则i所分配的内存最⼩地址那个字节中就存着1,其他字节是0.⼤端的话则1在i的最⾼地址字节处存放,char是⼀个字节,所以强制将char型量p指向i则p指向的⼀定是i的最低地址,那么就可以判断p中的值是不是1来确定是不是⼩端。
大端和小端概念、区别、转换以及辨别方法解析
大端和小端概念、区别、转换以及辨别方法解析
学习底层编程或逆向的童鞋,肯定对这两个名词并不陌生吧?!今天就给大家介绍一下这两个概念.
科普:
大端和小端,老外叫Big-Endian 和Little-Endian,其实指的都是同一个东东在计算机界,表示数据在存储器中的存放顺序。
不同的CPU、操作系统对待数据的存储方式各有不同,但一般常见的操作系统都是小端,而通讯协议则是大端。
但并不是说系统是小端形式存储,文件就一定要采用小端的形式,不同的应用程序对于自身数据的存储方式也各有千秋(自家数据爱咋放咋放,有些还打乱了加密呢),比如:
* Adobe PS -- 大端
* BMP -- 小端
* GIF -- 小端
* JPEG -- 大端
* MacPaint -- 大端
* RTF -- 小端
那么小端和大端有什么区别呢?举个栗子大家就明了了:
比如0x12345678 这个数:
* 大端法在内存中按字节依次存放为:12 34 56 78
* 小端法在内存中按字节依次存放为:78 56 34 12
解释:
大端:较高的有效字节存放在较低的存储器地址,较低的有效字节存放在较高的存储器地址。
比如整型变量0x12345678 占 4 个字节,那么根据内存地址从小到大它们的存放方式如下:。
大端(Big Endian)与小端(Little Endian)详解大端(Big Endian)与小端(Little Endian)简介///////////////////////////////////////////////////////1. 你从哪里来?端模式(Endian)的这个词出自Jonathan Swift书写的《格列佛游记》。
这本书根据将鸡蛋敲开的方法不同将所有的人分为两类,从圆头开始将鸡蛋敲开的人被归为Big Endian,从尖头开始将鸡蛋敲开的人被归为Littile Endian。
小人国的内战就源于吃鸡蛋时是究竟从大头(Big-Endian)敲开还是从小头(Little-Endian)敲开。
在计算机业Big Endian和Little Endian也几乎引起一场战争。
在计算机业界,Endian表示数据在存储器中的存放顺序。
采用大端方式进行数据存放符合人类的正常思维,而采用小端方式进行数据存放利于计算机处理。
下文举例说明在计算机中大小端模式的区别。
//////////////////////////////////////////////////////2. 读书百遍其义自见小端口诀: 高高低低-> 高字节在高地址, 低字节在低地址大端口诀: 高低低高-> 高字节在低地址, 低字节在高地址long test = 0x313233334;小端机器:低地址-->高地址00000010: 34 33 32 31 -> 4321大端机器:低地址-->高地址00000010: 31 32 33 34 -> 4321test变量存储的是的0x10这个地址,那编译器怎么知道是读四个字节呢? -> 根据变量test的类型long可知这个变量占据4个字节.那编译器怎么读出这个变量test所代表的值呢? -> 这就根据是little endian 还是big endian来读取所以, 小端, 其值为0x31323334; 大端, 其值为0x34333231htonl(test) 的情况: ->其值为: 0x34333231小端机器:00000010: 31 32 33 34 -> 1234大端机器:00000010: 34 33 32 31 -> 4321//////////////////////////////////////////////////////////////////////////// /////////3. 拿来主义Byte Endian是指字节在内存中的组织,所以也称它为Byte Ordering,或Byte Order。
对于数据中跨越多个字节的对象,我们必须为它建立这样的约定:(1) 它的地址是多少?(2) 它的字节在内存中是如何组织的?针对第一个问题,有这样的解释:对于跨越多个字节的对象,一般它所占的字节都是连续的,它的地址等于它所占字节最低地址。
(链表可能是个例外,但链表的地址可看作链表头的地址)。
比如: int x,它的地址为0x100。
那么它占据了内存中的Ox100,0x101,0x102,0x103这四个字节(32位系统,所以int占用4个字节)。
上面只是内存字节组织的一种情况: 多字节对象在内存中的组织有一般有两种约定。
考虑一个W位的整数。
它的各位表达如下:[Xw-1,Xw-2, ... ,X1,X0],它的MSB (Most Significant Byte,最高有效字节)为[Xw-1,Xw-2, ... Xw-8];LSB (Least Significant Byte,最低有效字节)为[X7,X6,...,X0]。
其余的字节位于MSB,LSB之间。
LSB和MSB谁位于内存的最低地址,即谁代表该对象的地址?这就引出了大端(Big Endian)与小端(Little Endian)的问题。
如果LSB在MSB前面,既LSB是低地址,则该机器是小端; 反之则是大端。
DEC (Digital Equipment Corporation,现在是Compaq公司的一部分)和Intel的机器(X86平台)一般采用小端。
IBM,Motorola(Power PC),Sun的机器一般采用大端。
当然,这不代表所有情况。
有的CPU即能工作于小端,又能工作于大端,比如ARM,Alpha,摩托罗拉的PowerPC。
具体情形参考处理器手册。
具体这类CPU是大端还是小端,应该和具体设置有关。
(如,Power PC支持little-endian字节序,但在默认配置时是big-endian 字节序)一般来说,大部分用户的操作系统(如windows, FreeBsd,Linux)是Little Endian的。
少部分,如MAC OS ,是Big Endian 的。
所以说,Little Endian还是Big Endian与操作系统和芯片类型都有关系。
因此在一个处理器系统中,有可能存在大端和小端模式同时存在的现象。
这一现象为系统的软硬件设计带来了不小的麻烦,这要求系统设计工程师,必须深入理解大端和小端模式的差别。
大端与小端模式的差别体现在一个处理器的寄存器,指令集,系统总线等各个层次中。
Linux系统中,你可以在/usr/include/中(包括子目录)查找字符串BYTE_ORDER(或_BYTE_ORDER, __BYTE_ORDER),确定其值。
BYTE_ORDER中文称为字节序。
这个值一般在endian.h或machine/endian.h文件中可以找到,有时在feature.h中,不同的操作系统可能有所不同。
【用函数判断系统是Big Endian还是Little Endian】enum {FALSE = 0, TRUE = !FALSE};typedef short BOOL;BOOL IsBig_Endian()//如果字节序为big-endian,返回true;//反之为little-endian,返回false{unsigned short test = 0x1122;if(*( (unsigned char*) &test ) == 0x11)return TRUE;elsereturn FALSE;}//IsBig_Endian()//////////////////////////////////////////////////////////////////////////// //可以做个实验在windows上下如下程序#include <stdio.h>#include <assert.h>void main( void ){short test;FILE* fp;test = 0x3132; //(31ASIIC码的’1’,32ASIIC码的’2’)if ((fp = fopen ("c:\\test.txt", "wb")) == NULL)assert(0);fwrite(&test, sizeof(short), 1, fp);fclose(fp);}然后在C盘下打开test.txt文件,可以看见内容是21,而test等于0x3132,可以明显的看出来x86的字节顺序是低位在前.如果我们把这段同样的代码放到(big-endian)的机器上执行,那么打出来的文件就是12.这在本机中使用是没有问题的.但当你把这个文件从一个big- endian机器复制到一个little-endian 机器上时就出现问题了.如上述例子,我们在big-endian的机器上创建了这个test文件,把其复制到little-endian的机器上再用fread读到一个short里面,我们得到的就不再是0x3132而是0x3231了,这样读到的数据就是错误的,所以在两个字节顺序不一样的机器上传输数据时需要特别小心字节顺序,理解了字节顺序在可以帮助我们写出移植行更高的代码.正因为有字节顺序的差别,所以在网络传输的时候定义了所有字节顺序相关的数据都使用big-endian,BSD的代码中定义了四个宏来处理:#define ntohs(n) //网络字节顺序到主机字节顺序n代表net, h代表host, s代表short#define htons(n) //主机字节顺序到网络字节顺序n代表net, h代表host, s代表short#define ntohl(n) //网络字节顺序到主机字节顺序n代表net, h代表host, s代表long#define htonl(n) //主机字节顺序到网络字节顺序n代表net, h代表host, s代表long举例说明下这其中一个宏的实现:#define sw16(x) \((short)( \(((short)(x) & (short)0x00ffU) << 8) | \(((short)(x) & (short)0xff00U) >> 8) ))这里实现的是一个交换两个字节顺序.其他几个宏类似.我们改写一下上面的程序#include <stdio.h>#include <assert.h>#define sw16(x) \((short)( \(((short)(x) & (short)0x00ffU) << 8) | \(((short)(x) & (short)0xff00U) >> 8) ))#define sw32(x) \((long)( \(((long)(x) & (long)0x000000ff) << 24) | \(((long)(x) & (long)0x0000ff00) << 8) | \(((long)(x) & (long)0x00ff0000) >> 8) | \(((long)(x) & (long)0xff000000) >> 24) ))// 因为x86下面是低位在前,需要交换一下变成网络字节顺序#define htons(x) sw16(x)#define htonl(x) sw32(x)void main( void ){short test;FILE* fp;test = htons(0x3132); //(31ASIIC码的’1’,32ASIIC码的’2’)if ((fp = fopen ("c:\\test.txt", "wb")) == NULL)assert(0);fwrite(&test, sizeof(short), 1, fp);fclose(fp);}如果在高字节在前的机器上,由于与网络字节顺序一致,所以我们什么都不干就可以了,只需要把#define htons(x) sw16(x)宏替换为#define htons(x) (x).一开始我在理解这个问题时,总在想为什么其他数据不用交换字节顺序?比如说我们write一块buffer到文件,最后终于想明白了,因为都是unsigned char 类型一个字节一个字节的写进去,这个顺序是固定的,不存在字节顺序的问题【大端(Big Endian)与小端(Little Endian)简介】Byte Endian是指字节在内存中的组织,所以也称它为Byte Ordering,或Byte Order。