字节序

intel系列的cpu是小尾(Little Endian),高位的字节放在前面,低位的字节放到后面,如果网络传输等等需要进行字节转换,因为最初是摩托罗拉系列的cpu(主要应用于工控机,arm系列或者power pc系列)先出来混的,他们的cpu是相反的,大尾(Big Endian)的,所以他们的cpu在网络上传输是不需要转换的
1       字节序

          大多数处理器中内存是可以以字节为单位进行寻址的,当数据类型(比如int、long)大于1个字节时,其所占的字节在内存中的顺序存在两种模式,分别是小端模式(Little Endian)和大端模式(Big Endian)。小端模式是低位字节放在低地址,而大端模式则是高位字节放在低地址。

由于不同的计算机系统采用不同的字节序存储数据,同样一个4字节的32位整数,在内存中存储的方式就不同. 字节序分为小尾字节序(Little Endian)和大尾字节序(Big Endian), Intel处理器大多数使用小尾字节序, Motorola处理器大多数使用大尾(Big Endian)字节序;

小尾就是低位字节排放在内存的低端,高位字节排放在内存的高端。例如在32位处理器上,一个int类型占4个字节。一个4个字节的值为0x1234567的整数占与高低字节对应关系:
01
23
45
67
Byte3
Byte2
Byte1
Byte0
高位字节-- à --------- à -------------- à 低位字节
 
将在内存中按照如下顺序排放:
内存地址序号
字节在内存中的地址
16 进制值
0x03
Byte3
01
0x02
Byte2
23
0x01
Byte1
45
0x00
Byte0
67
 
 
大尾就是高位字节排放在内存的低端,低位字节排放在内存的高端。例如一个4字节的值为0x1234567的整数与高低字节对应关系:
01
23
45
67
Byte3
Byte2
Byte1
Byte0
高位字节-- à --------- à -------------- à 低位字节
将在内存中按照如下顺序排放:
内存地址序号
字节在内存中的地址
16 进制值
0x03
Byte0
67
0x02
Byte1
45
0x01
Byte2
23
0x00
Byte3
01
 
字节序是很重要的,一段代码在小端模式的处理器上运行是正确的,可能在大端模式的处理器上运行得到的是错误的结果。
        例如代码:
#include<stdio>

void foo(short * _p_module_id)
{
    printf("foo():module ID is %d.\n", * _p_module_id);
    * _p_module_id=4;
}

int main()
{
    int module_id=3;
    foo((short *)module_id);
    printf("main():module ID is %d.\n", module_id);
    return 0;
}
假设module_id的值存储在起始地址为0x10000的连续的4个字节的存储单元中。注意,由于代码中的数值较小,只占用了4个字节中的最低一个字节。
    注意,代码中的module_id=3为int型,占4个字节。按高地位顺序排列应该为:0,0,0,3. 而* _p_module_id=4为short型,只占2个字节,按高地位顺序排列为:0,4.
    在小端模式下,开始时0x10000到0x10003中依次存放的是3,0,0,0.当调用函数foo()时是址传递,即short * _p_module_id=0x10000; 这时数值在存储单元中的存储顺序没有变动。运行* _p_module_id=4; 由于_p_module_id为short类型,只占2个字节,因此这时0x10000和0x10001中的内容被更换为4,0而高地址0x10002和0x10003中的数据不变. foo()函数返回后,存储单元从0x10000到0x10001中的内容变为4,0,0,0. 这样输出时满足代码所要实现的要求。
    在大端模式下,开始时0x10000到0x10003中依次存放的是0,0,0,3.当调用函数foo()时是址传递,即short * _p_module_id=0x10000; 这时数值在存储单元中的存储顺序没有变动。运行* _p_module_id=4; 由于_p_module_id为short类型,只占2个字节,因此这时0x10000和0x10001中的内容被更换为0,4而高地址0x10002和0x10003中的数据不变. foo()函数返回后,存储单元从0x10000到0x10001中的内容变为0,4,0,3. 这样输出的结果是错误的。
     所以,为了避免所编写的程序存在移植性问题,在软件开发过程中指针应当严格按照所需要的类型进行传递。
 
2      网络字节序
TCP/IP 各层协议将字节序定义为大尾,因此TCP/IP协议中使用的字节序通常称之为网络字节序
3      字串在内存中的存储(intel系列)
    字串和整数是相反的,是安字串的索引从低到高存储到内存中的;
     char s[4] = “abc”;  
a
b
c
\0
s[0]
s[1]
s[2]
s[3]
 
 
将在内存中按照如下顺序排放:
内存地址序号
16 进制值
指针P的位置
0xbffeadf7
\0
p+3
0xbffeadf6
c
p+2
0xbffeadf5
b
p+1
0xbffeadf4
a
p
 
int main(void)
{
     char s[4] = "abc";
     char *p = s; 
     printf("%02x, %02x,    %02x,    %02x\n", &s[0], &s[1], &s[2], &s[3]);
     printf("%02x, %02x,    %02x,    %02x\n", p, p+1, p+2, p+3);
     printf("%c,   %c, %c, %c\n", s[0], s[1], s[2], s[3]);
        
     return 0;
}
输出结果:
[netcool@HFINMSP2 demo]$ ./demo001
bffeadf4,       bffeadf5,       bffeadf6,       bffeadf7
bffeadf4,       bffeadf5,       bffeadf6,       bffeadf7
a,      b,      c,
4      整数数组在内存中的存储(intel系列)
同字串一样,但是数组里的每一个整数的存储是按照小尾字节序;
 
5      linux系统中的处理方法
网络字节序作为一个标准字节序,如果系统并没有提供相关的转换函数,我们可以通过以下4个宏实现本地字节序和网络字节序的相互转换:
htons(): 将16位无符号整数从本地字节序转换成网络字节序
htonl(): 将32位无符号整数从本地字节序转换成网络字节序
ntohs(): 将16位无符号整数从网络字节序转换成本地字节序
ntohl(): 将32位无符号整数从网络字节序转换成本地字节序
 
网络字节序与主机字节序
 
        不同的CPU有不同的字节序类型 这些字节序是指整数在内存中保存的顺序 这个叫做主机序
       最常见的有两种:
       1. Little endian:将低序字节存储在起始地址
       2. Big endian:将高序字节存储在起始地址
 
        LE little-endian
       最符合人的思维的字节序
       地址低位存储值的低位
       地址高位存储值的高位
       怎么讲是最符合人的思维的字节序,是因为从人的第一观感来说低位值小,就应该放在内存地址小的地方,也即内存地址低位反之,高位值就应该放在内存地址大的地方,也即内存地址高位
 
       BE big-endian
       最直观的字节序
       地址低位存储值的高位
       地址高位存储值的低位
      为什么说直观,不要考虑对应关系,只需要把内存地址从左到右按照由低到高的顺序写出把值按照通常的高位到低位的顺序写出两者对照,一个字节一个字节的填充进去例子:
        在内存中双字0x01020304(DWORD)的存储方式内存地址:
                4000 4001 4002 4003
          LE 04 03 02 01
         BE 01 02 03 04
      例子:如果我们将0x1234abcd写入到以0x0000开始的内存中,则结果为:
                                 big-endian little-endian
                  0x0000      0x12             0xcd
                  0x0001      0x23             0xab
                  0x0002      0xab             0x34
                  0x0003      0xcd             0x12
        x86系列CPU都是little-endian的字节序。
 
        网络字节顺序是TCP/IP中规定好的一种数据表示格式,它与具体的CPU类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释。网络字节顺序采用big endian排序方式。
 
        为了进行转换 bsd socket提供了转换的函数 有下面四个:
        htons 把unsigned short类型从主机序转换到网络序
        htonl 把unsigned long类型从主机序转换到网络序
        ntohs 把unsigned short类型从网络序转换到主机序
        ntohl 把unsigned long类型从网络序转换到主机序
        在使用little endian的系统中,这些函数会把字节序进行转换
        在使用big endian类型的系统中,这些函数会定义成空宏
 
        同样,在网络程序开发时或是跨平台开发时 也应该注意保证只用一种字节序 不然两方的解释不一样就会产生bug。
注:1、网络与主机字节转换函数:htons ntohs htonl ntohl (s 就是short l是long h是host n是network)
        2、不同的CPU上运行不同的操作系统,字节序也是不同的,参见下表。
      处理器                 操作系统           字节排序
      Alpha                       全部            Little endian
      HP-PA                       NT             Little endian
      HP-PA                      UNIX          Big endian
      Intelx86                    全部            Little endian <-----x86系统是小端字节序系统
      Motorola680x()       全部            Big endian
      MIPS                          NT              Little endian
      MIPS                        UNIX            Big endian
      PowerPC                  NT              Little endian
      PowerPC                非NT            Big endian <-----PPC系统是大端字节序系统
      RS/6000                 UNIX             Big endian
      SPARC                   UNIX             Big endianI
      XP1200 ARM核心 全部              Little endian
 
 网络字节序与主机字节序区别:
        网络字节顺序是TCP/IP中规定好的一种数据表示格式,它与具体的CPU类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释。网络字节顺序采用big endian排序方式。主机字节序就是我们平常说的大端和小端模式:不同的 CPU 有不同的字节序类型,这些字节序是指整数在内存中保存的顺序这个叫做主机序。
 
****************************************************************************************************************************************************************************************
对字节序的测试:
1.测试编辑器属哪种模式

下面这段代码可以用来测试一下你的编译器是大端模式还是小端模式:

	short int x;
	char x0, x1;
	
	x = 0x1122;
	x0 = ((char*)&x)[0]; // 低地址单元
	x1 = ((char*)&x)[1]; // 高地址单元
	printf("x0 = 0x%x, x1 = 0x%x /n", x0, x1);	// x0 = 0x22, x1 = 0x11

输出:x0 = 0x22, x1 = 0x11

则表示当前编译器使用的是小端模式 

2.使用C语言判断处理器的大小端

int checkCPU() 
{
	{
		union w
		{
			int a;
			char b;
		} c;
		
		c.a = 1;
		
		return (c.b == 1);
	}
}

printf("checkCPU:%d/n", checkCPU());		// Output "checkCPU:1"

输出:checkCPU:1

表示处理器为小端模式。

 ***************************************************************************************************************************************************************************************

下面代码示范了两种检测系统字节序的方法。

第一种方法,使用强制类型转换的方式。C 语言在把占用2个字节的 short 变量强制转换为 char 之后,会把 short 变量的首地址赋给 char 变量,可以根据 char 变量的值判断系统字节序是 大端 还是 小端。

第二种方法,利用 联合类型 的特性。联合类型 共享同一段内存,首地址是相同的。

测试环境:
      ubuntu 10.04 amd 64.

/*******************
 * 文件名:endian.c
 ******************/
#include <stdio.h>

/*********************************************************
 * 使用类型的强制转换实现little-endian与big-endian的判断 
 *********************************************************
 * 返回值:                                              
 *          1 表示是小端字节序。                         
 *          0 表示不是小端字节序。                       
 *********************************************************/
int is_little_endian_a(void)
{ 
    unsigned short flag = 0x4321;
    if(*(unsigned char*)&flag == 0x21)
        return 1;
    else
        return 0;
}

/*********************************************************************************
 * 利用联合的特点来判断little-endian与big-endian
 *********************************************************************************
 * 返回值: 
 *          1   表示是小端字节序。
 *          0   表示是大端字节序。
 *          -1  表示不能使用这种方法确定字节序。比如有的机器的 short 长度不是 2 。
 ********************************************************************************/
int is_little_endian_b(void)
{
    union endian_un{
        short var;
        char bits[sizeof(short)];
    };

    union endian_un flag;
    flag.var=0x0102;

    //判断低位和高位的存储内容,确定是何种方式
    if(sizeof(short) == 2){
        if(flag.bits[0] == 1 && flag.bits[1] == 2)
            return 0;
        else if(flag.bits[0] == 2 && flag.bits[1] == 1)
            return 1;
        else
            return -1;
    } 
    
    return -1;
}

int main(void)
{
    int type = 0;

    type = is_little_endian_a();
    if (1 == type)
        printf("judged by first method, little-endian\n");
    else if (0 == type)
        printf("judged by first method, big-endian\n");

    type = is_little_endian_b();
    if (1 == type)
        printf("judged by second method, little-endian\n");
    else if (0 == type)
        printf("judged by second method, big-endian\n");
    else
        printf("can't judge it\n");

    return 0;
}

运行截图

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值