大端和小端

以前没理解大端和小端,详解大端模式和小端模式这里对于概念上讲得很详细,再次温习了一下。

对于linux,以我工作中使用的centos为例,在头文中/usr/include/endian.h中我看到如下定义:

/* Definitions for byte order, according to significance of bytes,
   from low addresses to high addresses.  The value is what you get by
   putting '4' in the most significant byte, '3' in the second most
   significant byte, '2' in the second least significant byte, and '1'
   in the least significant byte, and then writing down one digit for
   each byte, starting with the byte at the lowest address at the left,
   and proceeding to the byte with the highest address at the right.  */

#define __LITTLE_ENDIAN 1234
#define __BIG_ENDIAN    4321

这里定义了字节序,从左边到右边,内存地址由低到高,依次存放顺序如果为:最低有效位-次有效位-有效位-最高有效位,则定义为小端。数字4表示最高有效位,从4到1,依次递减。
理清概念时, 一个是要注意内存地址的方向,另一个是要明白字节的存放位置。
这样的话,对于一个十六进制表示的数:0x1234,需要2个字节来存放,放到低位字节和高位字节。

  1. 小端:低位字节(0x34),高位字节(0x12)。
  2. 大端:低位字节(0x12),高位字节(0x34)。跟字符串的顺序类似。

可以用代码验证:

#include <stdio.h>

int main()
{

    // 方法1
    int a = 0x1234;
    // 通过将int强制类型转换成char单字节,通过判断起始存储位置
    // 即把a的低字节上的内容复制给b
    char b = *(char*)&a;
    if (b == 0x12){
        printf("big endian\n");
    }
    if (b == 0x34){
        printf("little endian\n");
    }


    // 方法2
    // 联合体union的存放顺序是所有成员都从低地址开始存放,
    // 利用该特性可以轻松地获得了CPU对内存采用Little-endian还是Big-endian模式
    union Num 
    {
        int a;
        char b; //低地址字节
    };

    union Num num;
    num.a = 0x1234;

    if (num.b == 0x12){
        printf("big endian\n");
    }
    if (num.b == 0x34){
        printf("little endian\n");
    }


    // 方法3
    // 通过宏来判断
#if __BYTE_ORDER != __LITTLE_ENDIAN
    printf("big endian\n");
#endif

#if __BYTE_ORDER == __LITTLE_ENDIAN
    printf("little endian\n");
#endif


    return 0;
}

方法3可能更实用些,在头文件/usr/include/bits/endian.h中如下定义:

/* x86_64 is little-endian.  */

#define __BYTE_ORDER __LITTLE_ENDIAN

x86架构的cpu,使用的是小端,而网络字节编码使用的是大端,根据通信协议,程序发送数值型的数据,需要转成网络字节序(大端),可以使用函数:

#include <arpa/inet.h>

uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);

程序从网络上接收数值型的数据,按协议,需要把数据从网络字节序(大端)转成小端,可以使用:

#include <arpa/inet.h>

uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);

函数非常容易记忆: n表示network,h表示host,to表示转换方向。

若不转换,进程里的数值:0x1234,到了网络就是:0x3412。

对于mysql,有时候需要保存一个点分表示法的ip到数据库,但可以把ip转为数值型,入库使用mysql的函数inet_aton('1.2.3.4'), sql会转成数值16909060, 逆操作则是inet_ntoa(16909060)

mysql> select inet_aton('1.2.3.4');
+----------------------+
| inet_aton('1.2.3.4') |
+----------------------+
|             16909060 |
+----------------------+

mysql> select inet_ntoa(16909060);
+---------------------+
| inet_ntoa(16909060) |
+---------------------+
| 1.2.3.4             |
+---------------------+

同样的,n表示network,a表示a dot点分法。



– EOF –

Categories: in_think
Tags: c