数字常量
16 进制的常量通常都用作掩码或特殊位的值。如果一个没有后缀的 16 进制的常量是 32 位的,并且其高位被置位了,那么它就可以作为无符号整型进行定义。
例如,常数 OxFFFFFFFFL 是一个有符号的 long 类型。在 32 位系统上,这会将所有位都置位(每位全为 1),但是在 64 位系统上,只有低 32 位被置位了,结果是这个值是 0x00000000FFFFFFFF.
如果我们希望所有位全部置位,那么一种可移植的方法是定义一个有符号的常数,其值为 -1.这会将所有位全部置位,因为它采用了二进制补码算法。
可能产生的另外一个问题是最高位的设置。在 32 位系统上,我们使用的是常量 0x80000000。但是可移植性更好的方法是使用一个位移表达式:
1L << ((sizeof(long) * 8) - 1);
|
Endianism
Endianism 是指用来存储数据的方法,它定义了整数和浮点数据类型中是如何对字节进行寻址的。
Little-endian 是将低位字节存储在内存的低地址中,将高位字节存储在内存的高地址中。
Big-endian 是将高位字节存储在内存的低地址中,将低位字节存储在内存的高地址中。
表 3 给出了一个 64 位长整数的布局示例。
表 3. 64 位 long int 类型的布局
| |
低地址 |
|
|
|
|
|
|
高地址 |
| Little endian |
Byte 0 |
Byte 1 |
Byte 2 |
Byte 3 |
Byte 4 |
Byte 5 |
Byte 6 |
Byte 7 |
| Big endian |
Byte 7 |
Byte 6 |
Byte 5 |
Byte 4 |
Byte 3 |
Byte 2 |
Byte 1 |
Byte 0 |
例如,32 位的字 0x12345678 在 big endian 机器上的布局如下:
表 4. 0x12345678 在 big-endian 系统上的布局
| 内存偏移量 |
0 |
1 |
2 |
3 |
| 内存内容 |
0x12 |
0x34 |
0x56 |
0x78 |
如果将 0x12345678 当作两个半字来看待,分别是 0x1234 和 0x5678,那么就会看到在 big endian 机器上是下面的情况:
表 5. 0x12345678 在 big-endian 系统上当作两个半字来看待的情况
| 内存偏移量 |
0 |
2 |
| 内存内容 |
0x1234 |
0x5678 |
然而,在 little endian 机器上,字 0x12345678 的布局如下所示:
表 6. 0x12345678 在 little-endian 系统上的布局
| 内存偏移量 |
0 |
1 |
2 |
3 |
| 内存内容 |
0x78 |
0x56 |
0x34 |
0x12 |
类似地,两个半字 0x1234 和 0x5678 如下所示:
表 7. 0x12345678 在 little-endian 系统上作为两个半字看到的情况
| 内存偏移量 |
0 |
2 |
| 内存内容 |
0x3412 |
0x7856 |
IXDBA.NET技术社区 下面这个例子解释了 big endian 和 little endian 机器上字节顺序之间的区别。
下面的 C 程序在一台 big endian 机器上进行编译和运行时会打印 “Big endian”,在一台 little endian 机器上进行编译和运行时会打印 “Little endian”。
清单 2. big endian 与 little endian
#include
main () {
int i = 0x12345678;
if (*(char *)&i == 0x12)
printf ("Big endian\n");
else if (*(char *)&i == 0x78)
printf ("Little endian\n");
}
|
Endianism 在以下情况中非常重要:
使用位掩码时
对象的间接指针地址部分
在 C 和 C 中有位域来帮助处理 endian 的问题。我建议使用位域,而不要使用掩码域或 16 进制的常量。有几个函数可以用来将 16 位和 32 位数据从 “主机字节顺序” 转换成 “网络字节顺序”。例如,htonl (3)、ntohl (3) 用来转换 32 位整数。类似地,htons (3)、ntohs (3) 用来转换 16 位整数。然而,对于 64 位整数来说,并没有标准的函数集。但是在 big endian 和 little endian 系统上,Linux 都提供了下面的几个宏: