大小端介绍、查看及转换

26 篇文章 2 订阅
6 篇文章 1 订阅

以前搞应用程序时,并不怎么需要关注大小端问题,虽然学习过但并没太在意。最近搞底层模块消息交互开发就遇到了,数据储存是小端,终端显示是大端,也就是字节顺序出现了问题,又学习了下,简单分享下。

1、大小端简介

这里说的大小端指数据在内存中的存放顺序,即字节序,常见的有两种:

  • 1)Big-Endian(大端字节序):高位字节排放在内存的低地址端,低位字节排放在内存的高地址端(符合人们日常阅读习惯);
  • 2)Little-Endian(小端字节序):低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。

有点绕,举例说明下:
现在有个32位的整数0x12345678

char*0xa0xb0xc0xd
大端模式0x120x340x560x78
小端模式0x780x560x340x12
(关于高低位字节,有效位文末有简单说明)

2查看当前设备字节序

不同机器的字节序是不同的,我们如何查看呢?
可以通过编程来测试下是大端还是小端模式。

#include <stdio.h>
/*设备大小端测试*/
int main(int argc,char *argv[]){
//法1:通过字节测试     
        unsigned int data = 0x12345678;
        char *p; 
        p = (char*)&data;
         
        printf("data = 0x%0x  \n",data);
        printf("*p = 0x%0x\n",*p);
        printf("*(P+1) = 0x%0x\n",*(p+1));
        printf("*(p+2) = 0x%0x\n",*(p+2));
        printf("*(p+3) = 0x%0x\n",*(p+3));
        
//法2:通过union测试  
        union {
                unsigned int i;
                char a[4];
        } arr;
        arr.a[0] = 0x12;
        arr.a[1] = 0x34;
        arr.a[2] = 0x56;
        arr.a[3] = 0x78;
        printf("i = 0x%0x\n",arr.i);
       
        return 0;
}        

运行后结果如下(可直接复制编译):

data = 0x12345678  
*p = 0x78
*(P+1) = 0x56
*(p+2) = 0x34
*(p+3) = 0x12

i = 0x78563412

结果显而易见,我的机器是是小端模式。

  • 1、法1,这里int类型是经过了转换,直接打出来没问题,但是用char指针按字节访问,就不对了,我们的高位字节0x12存放在了高位地址*(p+3),故为小端模式。
  • 2、法2,4个char类型数据,通过int指针读出来,也发现字节序有问题。另,对于组合union的数据可以自己学习下,
  • 3、另外这里也可以看出来我这里的堆栈生长方向(参见我另一篇《代码分析栈(stack)的生长方向》)。

3大小端转换

知道了机器是小端模式,不符合人们习惯,很多时候就需要将大小端进行转换(当然也可以不,程序员字节知道就行,但很多时候要展示给用户,或者要给上层应用处理,就需要转换下)。
很简单,基本是个程序员看到马上就能给做了,这里仍提供两种方式供参考:

  • 1、移位运算,效率比较。(以int为例)
uint32 Reverse32(uint32 x)
{
    return ((x&0x000000ff)<<24)|((x&0x0000ff00)<<8)|
		   ((x&0x00ff0000)>>8)|((x&0xff000000)>>24);
}
  • 2、通过char*读取后重组
uint32 Reverse32(uint32 x)
{
        int i;
        char *temp[4];
        for(i = 0;i < 4;i++){
                temp[i] = (char*)(&x) + i;
        }   
        return ((*temp[3]<<24)|(*temp[2]<<16)|(*temp[1]<<8)|*temp[0]);
}      

比较推荐移位运算吧。

  • 3、另外,为了增加代码的可移植性,在在判断大小端选择转换。
uint32 dev_to_cpu(uint32 data)
{
     unsigned int x = 1;
     if(*(char *)&x == 1)
         return data;//Little-Endian
     else
         return Reverse32(data);//Big-Endian 
}

4为什么会有这个问题

说实在的,统一了不就好了吗?就不用搞来搞去了,为啥会有这样的问题呢?

  • 1、两种方式没有绝对优劣
  • 2、历史遗留原因
    看了很多,比较合理的解释是:
    计算机中电路优先处理低位字节,效率比较高,因为计算机都是从低位开始的,所以计算机内部处理都是小端字节序。而人类人类读写数值的方法,习惯用大端字节序,所以除了计算机的内部处,其他的场理合都是大端字节序,比如:网络传输和文件储存时都是用的大端字节序。
    大小端字节序问题,最有可能是跟技术算硬件或软件的创造者们,在技术创立之初的一些技术条件或个人习惯有关。所以大小端问题,体现在实际的计算机工业应用来上,不同的操作系统和不同的芯片类型可能都会有不同。

PS:
1、高位字节和低位字节
存放最低的8位有效位的字节被称为最低有效位字节或低位字节,而存放最高的8位有效位的字节被称为最高有效位字节或高位字节。

高位字节 低位字节
↓----------------------------↓ ↓-----------------------↓
15 14 13 12 11 10 9. 8. 7. 6. 5. 4. 3. 2. 1. 0.

2、MSB和LSB:

  • MSB:MoST Significant Bit ------- 最高有效位
  • LSB:Least Significant Bit ------- 最低有效位
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值