UNP练习:确定主机字节序

确定主机字节序的程序

记录 UNP 学习第三章的确定主机字节序的程序,使用 C++ 语言编写。

程序如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>

#define CPU_VENDOR_OS "x86_64-unknown-linux-gnu"

using namespace std;

int main(){
union {
short s;
char c[sizeof(short)];
} un;

un.s = 0x0102;
cout << CPU_VENDOR_OS << ": ";
if ( sizeof(short) == 2 ){
if ( un.c[0] == 1 && un.c[1] == 2 )
cout << "big-endian" << endl;
else if ( un.c[0] == 2 && un.c[1] == 1 )
cout << "little-endian" << endl;
else
cout << "unknown" << endl;
}
else
cout << "sizeof(short) = " << sizeof(short) << endl;
return 0;
}

在我的 Ubuntu 16.04 虚拟机上运行上面的程序得到的结果是:

1
x86_64-unknown-linux-gnu: little-endian

这说明我的虚拟机在内存中存储数据的字节序是 小端字节序

上面定义的 CPU_VENDOR_OS 宏的值是可在 UNP 随书的源码得到的 config.h 头文件中查看。


大端模式与小端模式理解

考虑一个 16 位整数,它由 2 个字节组成。内存中存储这两个字节有两种方法:

  • 一种将低序字节存储在起始地址,这称为 小端字节序
  • 另一种将高序字节存储在起始地址,这称为 大端字节序

在上面的程序中,定义了一个联合体变量 un,包含一个短整形变量 s 和一个包含 2 个字符的字符数组 c。首先给 un.s 赋值 0x0102,这样在字符串数组 c 中存放的字符串对应的 ASCII 值为 0x0102 。高序字节为 0x01,低序字节为 0x02 。

如果系统是小端模式:

低序字节存储在起始地址,也就是 0x02 存放在数组的起始地址 un.c;高序字节存储在起始地址+1,即 0x01 存放在 un.c+1 中。即有一下等式:

1
2
un.c[0] == 0x02
un.c[1] == 0x01

如果系统是大端模式:

高序字节存储在起始地址,也就是 0x01 存放在数组的起始地址 un.c;低序字节存储在起始地址+1,即 0x02 存放在 un.c+1 中。有以下等式:

1
2
un.c[0] == 0x01
un.c[1] == 0x02

对于一个二进制或十六进制的值,如 0x01020304,位于左边的 0x01 字节是高序字节,位于右边 0x04 的是低序字节。


或者用以下更简单的程序来确定:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>

using namespace std;

int main(){
short val = 0x0102;
char *p = (char *) &val;

if (*p == 1 && *(p+1) == 2)
cout << "big-endian" << endl;
else if (*p == 2 && *(p+1) == 1)
cout << "little-endian" << endl;
return 0;
}

将短整形变量 val 的地址强制转换成字符型指针的地址 p。p 是低地址,(p+1)是高地址。0x01是 val 的高有效字节,0x02是 val 的低有效字节。则有一下两种情况:

  • *p == 0x01 && *(p+1) == 0x02:高有效字节存放在低地址,大端模式。
  • *p == 0x02 && *(p+1) == 0x01:低有效字节存放在低地址,小端模式。