6.15. 64 位下的可移植性

Tip

代码应该对 64 位和 32 位系统友好. 处理打印, 比较, 结构体对齐时应切记:

  • 对于某些类型, printf() 的指示符在 32 位和 64 位系统上可移植性不是很好. C99 标准定义了一些可移植的格式化指示符. 不幸的是, MSVC 7.1 并非全部支持, 而且标准中也有所遗漏, 所以有时我们不得不自己定义一个丑陋的版本 (头文件 inttypes.h 仿标准风格):
  1. // printf macros for size_t, in the style of inttypes.h
  2. #ifdef _LP64
  3. #define __PRIS_PREFIX "z"
  4. #else
  5. #define __PRIS_PREFIX
  6. #endif
  7.  
  8. // Use these macros after a % in a printf format string
  9. // to get correct 32/64 bit behavior, like this:
  10. // size_t size = records.size();
  11. // printf("%"PRIuS"\n", size);
  12. #define PRIdS __PRIS_PREFIX "d"
  13. #define PRIxS __PRIS_PREFIX "x"
  14. #define PRIuS __PRIS_PREFIX "u"
  15. #define PRIXS __PRIS_PREFIX "X"
  16. #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 作为后缀, 如:

  1. int64_t my_value = 0x123456789LL;
  2. uint64_t my_mask = 3ULL << 48;
  • 如果你确实需要 32 位和 64 位系统具有不同代码, 可以使用 #ifdef _LP64 指令来切分 32/64 位代码. (尽量不要这么做, 如果非用不可, 尽量使修改局部化)