6.15. 64 位下的可移植性
Tip
代码应该对 64 位和 32 位系统友好. 处理打印, 比较, 结构体对齐时应切记:
- 对于某些类型,
printf()
的指示符在 32 位和 64 位系统上可移植性不是很好. C99 标准定义了一些可移植的格式化指示符. 不幸的是, MSVC 7.1 并非全部支持, 而且标准中也有所遗漏, 所以有时我们不得不自己定义一个丑陋的版本 (头文件inttypes.h
仿标准风格):
- // printf macros for size_t, in the style of inttypes.h
- #ifdef _LP64
- #define __PRIS_PREFIX "z"
- #else
- #define __PRIS_PREFIX
- #endif
- // Use these macros after a % in a printf format string
- // to get correct 32/64 bit behavior, like this:
- // size_t size = records.size();
- // printf("%"PRIuS"\n", size);
- #define PRIdS __PRIS_PREFIX "d"
- #define PRIxS __PRIS_PREFIX "x"
- #define PRIuS __PRIS_PREFIX "u"
- #define PRIXS __PRIS_PREFIX "X"
- #define PRIoS __PRIS_PREFIX "o"
类型
不要使用
使用
备注
void *
(或其他指针类型)%lx
%p
int64_t
%qd, %lld
%"PRId64"
uint64_t
%qu, %llu, %llx
%"PRIu64", %"PRIx64"
size_t
%u
%"PRIuS", %"PRIxS"
C99 规定 %zu
ptrdiff_t
%d
%"PRIdS"
C99 规定 %zd
注意 PRI
宏会被编译器扩展为独立字符串. 因此如果使用非常量的格式化字符串, 需要将宏的值而不是宏名插入格式中. 使用 PRI
宏同样可以在 %
后包含长度指示符. 例如, printf("x = %30"PRIuS"\n", x)
在 32 位 Linux 上将被展开为 printf("x = %30" "u" "\n", x)
, 编译器当成 printf("x = %30u\n", x)
处理 (Yang.Y 注: 这在 MSVC 6.0 上行不通, VC 6 编译器不会自动把引号间隔的多个字符串连接一个长字符串).
记住
sizeof(void *) != sizeof(int)
. 如果需要一个指针大小的整数要用intptr_t
.你要非常小心的对待结构体对齐, 尤其是要持久化到磁盘上的结构体 (Yang.Y 注: 持久化 - 将数据按字节流顺序保存在磁盘文件或数据库中). 在 64 位系统中, 任何含有
int64t
/uint64t
成员的类/结构体, 缺省都以 8 字节在结尾对齐. 如果 32 位和 64 位代码要共用持久化的结构体, 需要确保两种体系结构下的结构体对齐一致. 大多数编译器都允许调整结构体对齐. gcc 中可使用__attribute
((packed))
. MSVC 则提供了#pragma pack()
和__declspec(align())
(YuleFox 注, 解决方案的项目属性里也可以直接设置).创建 64 位常量时使用 LL 或 ULL 作为后缀, 如:
- int64_t my_value = 0x123456789LL;
- uint64_t my_mask = 3ULL << 48;
- 如果你确实需要 32 位和 64 位系统具有不同代码, 可以使用
#ifdef _LP64
指令来切分 32/64 位代码. (尽量不要这么做, 如果非用不可, 尽量使修改局部化)