概述
□ 64位Windows系统下也能运行32位程序,是因为有一个WOW64子系统。它能将32位应用程序的API调用转换成对原生64位系统的调用。正是因为WOW64的存在,32位应用程序在64位系统下并不能发挥最佳的性能,反而比在32位系统下有大约2%的性能损失。如果开发的是64位应用程序,则不需要依赖WOW64运行,并可带来大约5%-15%的性能提升(5%-10%由于采用了64位架构,1%-5%由于未使用WOW64)。
□ 32位程序的最大地址空间是4GB,64位程序的最大地址空间是可以大于4GB的。
□ 64位程序不再区分cdecl、stdcall等各种调用方式。gcc在64位下面,不再支持__attribute((cdecl))__和__attribute((stdcall))__。
□ 64位程序不再使用ESP,而是使用增加的几个64位寄存器,因为ESP不支持64位空间的栈。
□ 虽然在64位Windows系统下可以运行64位和32位进程,但是64位代码和32位代码不能在相同进程上运行。你的代码要么全部是64位,要么全部是32位,要加载的库和组件也要满足这一要求。
数据类型字节长度
32位程序和64位程序,其数据类型的字节长度并不完全一致,可参看下表。
注意事项1
□ 使用“_WIN32”宏(不要用“WIN32”宏)来判断是不是Windows平台编译环境,使用“_WIN64”宏来区分编译环境是32位还是64位。
注意事项2
□ 64位整型数据在Windows和Linux中的输出格式不一样。
Windows下,可参考下面的示例代码。
__int64 a; printf("%I64d", a); unsigned __int64 b; printf("%I64u", b); printf("%Iu", c);
Linux下,可参考下面的示例代码。
long long a; printf("%lld",a); unsigned long long b; printf("%llu", b); size_t c; printf("%zu", c);
注意事项3
□ 谨慎使用printf、scanf等类似函数。
1、64位程序时,size_t与%u不匹配。下面的代码是不合理的。
const char *invalidFormat = "%u"; size_t value = SIZE_MAX; printf(invalidFormat, value);
2、64位程序时,指针为8个字节,转化为字符串需要16个字节,内存会溢出。下面的代码是不合理的。
char buf[9]; const char *pointer = "hello"; sprintf(buf, "%p", pointer);
注意事项4
□ 避免使用类似下面的魔法数字,尽量使用sizeof,或者<limits.h>、<inttypes.h>中定义的值。
以下为一些错误使用魔法数字的示例。
size_t ArraySize = N * 4; intptr_t *Array = (intptr_t *)malloc(ArraySize); size_t values[ARRAY_SIZE]; memset(values, 0, ARRAY_SIZE * 4); size_t n, r; n = n >> (32 - r); hFileMapping = CreateFileMapping((HANDLE)0xFFFFFFFF, NULL, PAGE_READWRITE, (DWORD) 0, (DWORD)(szBufIm), (LPCTSTR)&FileShareNameMap[0]);
正确的示例可参看下面的代码。
size_t ArraySize = N * sizeof(intptr_t); intptr_t *Array = (intptr_t *)malloc(ArraySize); size_t values[ARRAY_SIZE]; memset(values, 0, ARRAY_SIZE * sizeof(size_t)); // 或者 memset(values, 0, sizeof(values)); size_t n, r; n = n >> (CHAR_BIT * sizeof(n) - r); hFileMapping = CreateFileMapping((HANDLE)(LONG_PTR)-1, NULL, PAGE_READWRITE, (DWORD) 0, (DWORD)(szBufIm), (LPCTSTR)&FileShareNameMap[0]);
另外,可以通过下面的代码很清楚地看到,-1与魔法数字0xFFFFFFFF在64位程序下的输出值不一样。
using namespace std; void foo(void *ptr) { cout << ptr << endl; } int main() { cout << "-1\t\t"; foo((void *)-1); cout << "0xFFFFFFFF\t"; foo((void *)0xFFFFFFFF); return 0; }
注意事项5
□ 不要在整型和指针/句柄间进行转换。如果确实需要转换,请使用intptr_t或uintptr_t。intptr_t和uintptr_t是为了跨平台,其长度总是所在平台的位数,所以用来存放地址。
以下为一些错误的示例。