概述
□ 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位程序,其数据类型的字节长度并不完全一致,可参看下表。
数据类型 | 32位系统 | 64位系统 |
char/bool | 1 | 1 |
short | 2 | 2 |
int | 4 | 4 |
long int | 4 | Windows:4,Linux:8 |
long long int | 8 | 8 |
size_t | 4 | 8 |
pointer/handle | 4 | 8 |
float | 4 | 4 |
double | 8 | 8 |
time_t | 8(VC2005之前:4) | 8 |
ptrdiff_t | 4 | 8 |
注意事项1
□ 使用“_WIN32”宏(不要用“WIN32”宏)来判断是不是Windows平台编译环境,使用“_WIN64”宏来区分编译环境是32位还是64位。
常量/宏定义 | 预定义选项 | Windows.h | VC编译器 |
WIN32 | 支持 | 支持(在minwindef.h中定义) | 不支持 |
_WIN32 | 不支持 | 不支持 | 支持 |
_WIN64 | 不支持 | 不支持 | 仅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>中定义的值。
魔法数字 | 使用场景 |
4 | 某个数据类型的字节数 |
32 | 某个数据类型的位数 |
0x7fffffff | 4 |
0x80000000 | 4 |
0xffffffff | 8 |
以下为一些错误使用魔法数字的示例。
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位程序下的输出值不一样。
#include <iostream>
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;
}
32位程序时的输出如下。
-1 FFFFFFFF
0xFFFFFFFF FFFFFFFF
64位程序时的输出如下。
-1 FFFFFFFFFFFFFFFF
0xFFFFFFFF 00000000FFFFFFFF
注意事项5
□ 不要在整型和指针/句柄间进行转换。如果确实需要转换,请使用intptr_t或uintptr_t。intptr_t和uintptr_t是为了跨平台,其长度总是所在平台的位数,所以用来存放地址。
以下为一些错误的示例。
char *p;
p = (char *) ((int)p & PAGEOFFSET);
DWORD tmp = (DWORD)malloc(ArraySize);
int *ptr = (int *)tmp;
正确的示例可参看下面的代码。
char *p;
p = (char *) ((intptr_t)p & PAGEOFFSET);
DWORD_PTR tmp = (DWORD_PTR)malloc(ArraySize);
int *ptr = (int *)tmp;
注意事项6
□ 为了保证通用性,在进行数据交换时,尽量不要使用long、size_t等会随平台变化的类型,最好使用固定大小的数据类型。
以下为一些错误的示例。
size_t PixelsCount;
fread(&PixelsCount, sizeof(PixelsCount), 1, inFile);
__int32 value_1;
SSIZE_T value_2;
inputStream >> value_1 >> value_2;
正确的示例可参看下面的代码。
size_t PixelsCount;
__uint32 tmp;
fread(&tmp, sizeof(tmp), 1, inFile);
PixelsCount = static_cast<size_t>(tmp);
__int32 value_1;
__int32 value_2;
inputStream >> value_1 >> value_2;
注意事项7
□ 尽量不要在不兼容的类型上进行指针的转换。
我们来看看下面的示例。
#include <iostream>
using namespace std;
int main()
{
int array[4] = { 1, 2, 3, 4 };
size_t *sizetPtr = (size_t *)(array);
cout << sizetPtr[1] << endl;
return 0;
}
在上面的示例中,32位程序会输出:1,64位程序会输出:17179869187。这是因为,64位程序时,size_t为8个字节,指针转换后,sizetPtr的第二个元素的值为:0x0000000400000003。
注意事项8
□ 在联合体中谨慎使用指针。
我们来看看下面的示例。
union PtrNumUnion
{
char *m_p;
unsigned m_n;
} u;
u.m_p = str;
u.m_n += delta;
这段示例代码在32位程序时,运行正常。但在64位程序时,可能会导致运行异常甚至崩溃。在64位程序下,指针m_p为8个字节,m_n为4个字节,并不完全对应;如果用m_n作为指针来使用,会导致指针的值不正确。
注意事项9
□ 在进行移位操作时,要避免溢出。
我们来看看下面的示例。
ptrdiff_t SetBitN(ptrdiff_t value, unsigned bitNum)
{
ptrdiff_t mask = 1 << bitNum;
return value | mask;
}
32位程序时,bitNum从0到31时,移位后mask的值均正常。
64位程序时,bitNum取值范围一般为0到63;但当取值为32到63时,mask的值都是不正确的。
为什么会这样呢?因为常量数字1默认为int类型,当位移的位数大于等于32时,会发生溢出。溢出后,mask的值可能是1,也可能是0,具体要看编译器的实现。
正确的方法是:先把常量数字转换为需要的类型,再进行移位。可参考下面的代码。
ptrdiff_t SetBitN(ptrdiff_t value, unsigned bitNum)
{
ptrdiff_t mask = ptrdiff_t(1) << bitNum;
return value | mask;
}
标签:示例,32,程序开发,程序,value,64,out,size From: https://blog.51cto.com/u_16794707/12079935