大小端判断方法和转换

1.什么是大小端?

这是因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一个字节,一个字节为 8bit。但是在C语言中除了8bit的char之外,还有16bit的short型,32bit的long型(要看具体的编译器),另外,对于位数大于 8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如何将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。

2.大小端定义

大端Big Endian模式:即把数据的高字节放到低地址中
小端Little Endian模式:高字节放到高地址中

3.图解

画张图简单解释下大小端的区别,比如我们要存取一个0x12345678的数据,在大小端机器的存取方式分别是:

大端模式:

高位 -----------------> 低位

0x78 | 0x56 | 0x34 | 0x12

小端模式:

高位 ------------------> 低位
0x12 | 0x34 | 0x56 | 0x78


image.png

4 判断机器大小端方式

1.字符指针判断
在32位平台下,int占4个字节,而char类型的指针是占一个字节的,如果我们把int强传为char类型的指针,只会保存一个字节的数据,那么我们只需要判断char里面的第一个字节和int里面的第一个字节是否是一致即可判断。
如果一致则为小端模式,反之为大端模式。

下面代码我们令 int a=1 如果是小端模式,int下1会存放在在低地址处,而强传为char类型的指针,1也在低地址处,所以可以判断。

#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string>
#include <string.h>
#include <memory>
#include <stdint.h>

int main() {
    // judge  large or small

    uint64_t flag = 2;
    uint64_t *raw_ptr = &flag;

    char *p = reinterpret_cast<char*>(raw_ptr);
    
    if(*(p) == 2) {
        std::cout << "small" << std::endl;
    } else {
        std::cout << "large" << std::endl;
    }

    return 0;
}

image.png

2.联合体判断
由于联合体所有数据共享一块地址空间,存放数据的所有成员都是从低地址开始存放,所以我们可以在联合体内定义一个int和一个char类型变量,然后在外部实例化的时候创建int变量,用char变量调用,相当于隐式类型转化,如果结果为1,则低字节存放在低地址,既是小端机器,反之大端机器。

void small_large_union() {
    union flag{
        uint64_t flag1;
        char flag2;
    };

    flag f;
    f.flag1 =1;

    if(f.flag2 == 1) {
        std::cout << "small" << std::endl;
    } else {
        std::cout << "large" << std::endl;
    }
    
}

这里复习一下union
union 型数据所占的空间等于其最大的成员所占的空间。对 union 型的成员的存取都是相对于该联合体基地址的偏移量为 0 处开始,也就是联合体的访问不论对哪个变量的存取都是从 union 的首地址位置开始。

联合是一个在同一个存储空间里存储不同类型数据的数据类型。这些存储区的地址都是一样的,联合里不同存储区的内存是重叠的,修改了任何一个其他的会受影响。

共用体表示几个变量共用一个内存位置,在不同的时间保存不同的数据类型和不同长度的变量。在union中,所有的共用体成员共用一个空间,并且同一时间只能储存其中一个成员变量的值。

常见CPU的字节序

Big Endian : PowerPC、IBM、Sun
Little Endian : x86、DEC
ARM既可以工作在大端模式,也可以工作在小端模式。

5.大小端转换

法1:位运算

#include<stdio.h>  
  
typedef unsigned int uint_32 ;  
typedef unsigned short uint_16 ;  
 
//16位
#define BSWAP_16(x) \
    (uint_16)((((uint_16)(x) & 0x00ff) << 8) | \
              (((uint_16)(x) & 0xff00) >> 8) \
             )
             
//32位               
#define BSWAP_32(x) \
    (uint_32)((((uint_32)(x) & 0xff000000) >> 24) | \
              (((uint_32)(x) & 0x00ff0000) >> 8) | \
              (((uint_32)(x) & 0x0000ff00) << 8) | \
              (((uint_32)(x) & 0x000000ff) << 24) \
             )  
 
//无符号整型16位  
uint_16 bswap_16(uint_16 x)  
{  
    return (((uint_16)(x) & 0x00ff) << 8) | \
           (((uint_16)(x) & 0xff00) >> 8) ;  
}  
 
//无符号整型32位
uint_32 bswap_32(uint_32 x)  
{  
    return (((uint_32)(x) & 0xff000000) >> 24) | \
           (((uint_32)(x) & 0x00ff0000) >> 8) | \
           (((uint_32)(x) & 0x0000ff00) << 8) | \
           (((uint_32)(x) & 0x000000ff) << 24) ;  
}  
 
int main(int argc,char *argv[])  
{  
    printf("------------带参宏-------------\n");  
    printf("%#x\n",BSWAP_16(0x1234)) ;  
    printf("%#x\n",BSWAP_32(0x12345678));  
    printf("------------函数调用-----------\n");  
    printf("%#x\n",bswap_16(0x1234)) ;  
    printf("%#x\n",bswap_32(0x12345678));  
      
    return 0 ;  
}  
输出结果:
------------带参宏-------------
0x3412
0x78563412
------------函数调用-----------
0x3412
0x78563412

法2:arpa.inet.h网络库中有一些将数据转换为网络字节顺序的函数

htonl() //32位无符号整型的主机字节顺序到网络字节顺序的转换(小端->>大端)
htons() //16位无符号短整型的主机字节顺序到网络字节顺序的转换 (小端->>大端)
ntohl() //32位无符号整型的网络字节顺序到主机字节顺序的转换 (大端->>小端)
ntohs() //16位无符号短整型的网络字节顺序到主机字节顺序的转换 (大端->>小端)

注,主机字节顺序,X86一般多为小端(little-endian),网络字节顺序,即大端(big-endian);

举个栗子:

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <arpa/inet.h>

int main(int argc, char * argv[]) {
    union flag{
        int a;
        char b[2];
    };

    flag f;
    f.b[0] = 0x11;
    f.b[1] = 0x22;
    //0x2211 为小端  0x1122 为大端
    

    //htons
    printf("original: 0x%x\n",f.a);
    printf("trans:    0x%x\n",htons(f.a));

    return 0;
}
结果

6.大小端问题的来源

大多数编程语言有关于位域都有相关的定义,区别于我们常说的硬件有关的大小端,编译器也需要保证位分配顺序是确定、有迹可循的。

The order of allocation of bit-fields within a unit (C90 6.5.2.1[1], C99 6.7.2.1[2]).
而标准中规定如果一个结构体中含有位域,那么位域的写入顺序和当前系统字节序有关。也就是说编译器需要保证有位域的类型分配顺序。先定义的位域在大端环境从MSB开始分配,如果是小端环境则先定义的位域从LSB开始分配。
先补充两个关键词,MSB和LSB:
MSB:MoST Significant Bit ------- 最高有效位
LSB:Least Significant Bit ------- 最低有效位

struct bitfield {
    _u8 a:1;
    _u8 b:2;
    _u8 c:3;
}bf;
bf.a=1;
bf.b=2;
bf.c=3;

那么在大端序机器上

bf.a--、
bf.b--+--、
bf.c--+--+--、
      |  |  |
      |  |  |
      V  V  V
     +-+--+---+--+
      1 10 011 xx
     +-+--+---+--+
MSB                  LSB

在小端机器上

bf.a------------+
bf.b---------+  |
bf.c------+  |  |
          |  |  |
          |  |  |
          V  V  V
     +--+---+--+-+
      xx 011 10 1
     +--+---+--+-+
MSB                  LSB

在编译器层面保证了单个字节的IO顺序,实质上可以认为是从汇编/cpu层面对上层隐藏了这一问题。在FPGA或者其他领域,就要涉及到线路传输时,通常一个字节会被拆分成8个比特进行传输,此时MSB会先传输高位,而LSB将会先传输低位,这就是我们看到的不论大小端机器单个字节内顺序一致的原因。

理解了硬件上大小端的概念,就能很容易的掌握位域操作在大小端硬件上的分配布局,从而不会在编码时产生混淆。

处理器在硬件上由于端模式问题在设计中有所不同,主要是计算机业界早期发展先驱们做的取舍不同。大端序机器首先会读到高位,而高位一般会是符号位置,经常用来做标记,早期缓冲设备缓存很小的情况下,先收到高字节能快速判断报文信息,需要多大的缓存、ip掩码计算等等,而小端序则非常适合进行加法器,进位的顺序从地位加到高位依次进位就可以了,而大端序需要加载完全部字节。从系统的角度上看,端模式问题对软件和硬件的设计带来了不同的影响,当一个处理器系统中大小端模式同时存在时,必须要对这些不同端模式的访问进行特殊的处理。

TCP/IP协议的RFC7000[3]规定了网络传输使用大端序,也是因为早期网络设备缓冲区小,性能弱,采用了大端序,主导了网络协议。

7.Reference

[1].C89/C90 standard (ISO/IEC 9899:1990) 6.5.2.1
[2].C99 standard (ISO/IEC 9899:1999) 6.7.2.1

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,816评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,729评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,300评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,780评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,890评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,084评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,151评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,912评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,355评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,666评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,809评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,504评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,150评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,121评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,628评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,724评论 2 351

推荐阅读更多精彩内容

  • 详解大端模式和小端模式 嵌入式开发交流群280352802,欢迎加入! 一、大端模式和小端模式的起源 关于...
    l日月之明l阅读 1,064评论 0 0
  • __block和__weak修饰符的区别其实是挺明显的:1.__block不管是ARC还是MRC模式下都可以使用,...
    LZM轮回阅读 3,293评论 0 6
  • 最全的iOS面试题及答案 iOS面试小贴士 ———————————————回答好下面的足够了-----------...
    zweic阅读 2,695评论 0 73
  • ———————————————回答好下面的足够了---------------------------------...
    恒爱DE问候阅读 1,713评论 0 4
  • 多线程、特别是NSOperation 和 GCD 的内部原理。运行时机制的原理和运用场景。SDWebImage的原...
    LZM轮回阅读 2,004评论 0 12