Skip to content

64位下C程序的可移植性(64-bit Portability)


代码在64位和32位的系统中,原则上应该都比较友好,尤其对于输出、比较、结构对齐(structure alignment)来说:

1) 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)

2) 记住sizeof(void *) != sizeof(int),如果需要一个指针大小的整数要使用intptr_t

3) 需要对结构对齐加以留心,尤其是对于存储在磁盘上的结构体。在64位系统中,任何拥有int64_t/uint64_t成员的类/结构体将默认被处理为8字节对齐。如果32位和64位代码共用磁盘上的结构体,需要确保两种体系结构下的结构体的对齐一致。大多数编译器提供了调整结构体对齐的方案。gcc中可使用__attribute__((packed)),MSVC提供了#pragma pack()__declspec(align())

4) 创建64位常量时使用LLULL作为后缀,如:

int64_t my_value = 0x123456789LL;
uint64_t my_mask = 3ULL << 48;

5) 如果你确实需要32位和64位系统具有不同代码,可以在代码变量前使用。(尽量不要这么做,使用时尽量使修改局部化)。