彻底理解字节序

假设我们有一个16位整数,那么它是由2个字节组成的。

首先,我们引出一个概念,数据高字节数据低字节

特别要强调的是,这里的指的是数据表示形式上的高字节和低字节,不涉及(不依赖)任何存储上的具体实现。

因为后面我就会引入另一个概念,内存低地址内存高地址

由于计算机内存地址的基本单位是字节,也就是说每个字节占一个地址值。

那么16位整数需要2个自己的存储空间,即需要占用2个内存地址。

还有两个术语是我们会经常见到的,那就是MSB(most significant bit)和LSB(least significant bit)。

MSB和LSB都是针对数据表示形式而言的,同样也不涉及(不依赖)任何存储上的具体实现。

MSB指的是最高有效位,是数字最左边的一位。LSB指的是最低有效位,是数字最右边一位。

在讨论字节序问题上,如果文章上来不先把概念特别强调清楚,我们很容易产生混淆。

下面,我们将引出这篇文章最重要的概念了,小端(little-endian)和大端(big-endian)。

小端指的是将数据小的一端(即数据低字节)存储在起始地址(即低地址)。

小端指的是将数据大的一端(即数据高字节)存储在起始地址(即低地址)。

 我们把某个主机的CPU所用的字节序称为主机字节序

TCP/IP规定采用大端字节序来传送网络协议数据包中的多字节整数数据。也就是说网络字节序等于大端字节序。

下面我们要讲解位序了。位序与字节序一般是保持一致的。

在C语言中,位域与结构体类似,其语法规定:先声明的成员位于低地址,后声明的成员位于高地址
那么下面的位域中:
typedef struct OneByte
{
bt0 : 1;
bt1 : 1;
bt2 : 1;
bt3 : 1;
bt4 : 1;
bt5 : 1;
bt6 : 1;
bt7 : 1;
}
成员bt0就位于一个字节中最低地址bit0处,成员bt7就位于一个字节的最地址bit7处。

上面讲的是原理,下面讲一些具体实现。

宏定义__BYTE_ORDER定义在glibc的endian.h中,其值要么为__LITTLE_ENDIAN,要么为__BIG_ENDIAN

应用程序代码中可以用如下的语句来判断大小端,#if __BYTE_ORDER == __LITTLE_ENDIAN

但是,宏定义__BYTE_ORDER不一定存在于别的libc中,因此应用程序代码中在使用__BYTE_ORDER之前还需要实现判断libc的类型。

那么如何判断你的代码使用的libc是不是glibc呢?

使用这个宏定义进行判断#ifdef __GLIBC__ ,

The symbol __GLIBC__ is defined in the header features.h, and features.h is include by stdio.h

glibc-2.19-svnr25243/libc/ports/sysdeps/unix/sysv/linux/mips/bits/endian.h

里面通过判断__MIPSEB__还是__MIPSEL__来给__BYTE_ORDER定义不同的值。

#ifdef __MIPSEB__
# define __BYTE_ORDER __BIG_ENDIAN
#else
# ifdef __MIPSEL__
# define __BYTE_ORDER __LITTLE_ENDIAN
# endif
#endif

那么__MIPSEB__和__MIPSEL__来自何方呢?

还是在同样的头文件中,我看到如下注释,重点是我加粗的文字。

The MIPS architecture has selectable endianness.
Linux/MIPS exists in two both little and big endian flavours and we
want to be able to share the installed headerfiles between both,
so we define __BYTE_ORDER based on GCC's predefines.

这说明了__MIPSEB__和__MIPSEL__是定义在所用的交叉编译器里面的。

这里总结一下,glibc的大小端定义依赖于交叉编译器的预定义设置,而应用程序判断大小端又依赖于glibc的大小端定义。

原文地址:https://www.cnblogs.com/yorkwoo/p/4541151.html