Lessons on development of 64-bit C/C++ applications 笔记
一点说明: 凡是只有标题没有内容的, 是因为我认为其内容不重要.
Lesson 1. What 64-bit systems are
1. 首先澄清一个概念上的错误:
IA-64: Intel itanium microprocessor.
Intel 64: entension of x86 architecture with full backward compatibility. 这才是我们所要讨论的 64-bit 架构.You should understand that IA-64 and Intel 64 are absolutely different, incompatible with each other, microprocessor architectures.
原来 IA-64 和 Intel 64 是完全不相兼容的两个处理器架构.
2. Intel 64 和原先的 x86 比, 有如下优势
- the 64-bit address space;
- an extended register set;
- a command set familiar to developers;
- the capability to launch obsolete 32-bit applications in a 64-bit operating system;
- the capability to use 32-bit operating systems.
3. 在 Windows x64 中, 使用一种 WoW64(Windows-on-Windows 64) 方法实现与 32-bit 应用程序兼容. 这也是为什么 64 位 win 7 中, 注册表重定向引起编程时的很多困惑的原因.
4. 64-bit 理论寻址 16 Ebytes(2^64), 但实际上 64 位 windows 只能寻址 16 Tbytes(2^44), 但是不同版本的 windows 会限制其内存的最大值. 然而当今的 CPU 只能寻址 1Tbytes(2^40). 通过架构扩展可以使寻址能力到达 4 Pbytes(2^52).
5. 64-bit Windows 页面大小也是 4Kbytes. 进程空间中前 64 KB 的空间是不可用的, 所以进程的最低可用指针是 0x10000.
6. 64-bit 编译器有一个特性, 它只有唯一的调用约定, 即通过寄存器传参. 这取代了传统的栈传参, 加快了函数调用的速度. 然而在 32 位下, 存在很多调用约定. 如 __stdcall, __cdecl, __fastcall 等.
Lesson 2. Support of 32-bit applications in the 64-bit Windows environment
1. 64-bit Windows 运行 32 位程序会导致 2% 的速度下降. 因为 64 位操作系统通过 WoW64 运行 32 位程序. WoW64 是 32 位程序和 64 位 API 之间的一个间接层, 虽然这 "层" 很 "薄". 记住, 64 位系统运行 32 位程序速度是会下降的.
2. 使用 64 位编译器重新生成应用程序将会在 64 位 windows 中获得益处, 包括消除了 WoW64 间接层, 也为程序在寄存器使用上的到一些优化.
3. 64 位 Windows 存在注册表的重定向. 原因是 32 位应用程序不能使用 64 位的 Dll, 然而很多 32 位程序都会使用注册表中的环境变量, 通过该路径找到 dll. 因此 64 位操作系统将注册表中所有 32 位应用程序与 64 位应用程序分开. 如在 32 位应用程序访问注册表 HKEY_LOCAL_MACHINE\Software 时, 自动重定向到 Software\WOW6432Node 中. 32 位程序将获得 32 位 dll 的路径, 64 位应用程序将获得 64 位 dll 的路径. 以此实现兼容.
Lesson 3. Porting code to 64-bit systems. The pros and cons
1. 64 位是必然的趋势. 但是 64 位程序会让你在市场上缺少竞争力. 在你决定将程序转向 64 位前, 应该反复的问自己: 我有多少理由将现有的项目重编译为 64 位?
2. 软件向 64 位转移将会是一个非常缓慢和平滑的过程. 因此, 你不应该在 64 位模式开发一个生命周期很短的项目. 但是, 你完全进入 64 位开始的越晚, 那么你在今后的潜力就越小.
3. 由于 64 位 Windows 体系机构的变化以及寄存器的扩展, 使得 64 位程序在性能上有 5-15% 的提升. 此外, 程序还获得了巨大的进程空间, 这使对内存需求量大的程序获得极大的性能增长.
4. 16-bit 应用程序将不在 64-bit Windows 中支持. 但是你会发现某些 16-bit 应用程序还一个运行, 那是因为有些特殊的机制保证了这些流行的 16-bit 程序被其他版本所代替执行. 这个假象会误导你认为 16-bit 程序可以在 64-bit 环境下执行.
5. 将程序修改以移植为 64-bit 是一项非常大的花费.
Lesson 4. Creating the 64-bit configuration
1. 假设我们使用 vs 2008. 并非所有版本的 vs 08 都能够支持 64-bit 开发. VC 08 就不支持. vs standard 和 professional 都支持 64-bit 编译. vs Team System 支持 64-bit 编译和 64-bit 安腾处理器编译.
2. 环境配置步骤
1) 安装时确认在 Language Tools -> Visual C++ 中选择了 X64 Compilers and Tools 选项.
2) 在 Configuration Manager 中新建一个 platform.
3) 选择 x64 platform
4) 最后, 在你的程序生成框中, 选择 x64.
5) 在 Property Pages -> Linker -> System 中将 Stack Reserve Szie 设置为之前的 2-3 倍, 以保证程序的安全性.
Lesson 5. Building a 64-bit application
1. 64-bit 编程中有很多需要注意的问题. 其中最主要的就是将我们从 32-bit 编程的惯性中摆脱出来. 比如一下代码在 64-bit 编译器中是错误的:
void foo(unsigned char) {}
void foo(unsigned int) {}
void a(const char *str)
{
foo(strlen(str));
}
这段代码在 32-bit 模式下降顺利通过编译, 但是在 64-bit 模式下, vs 编译器产生如下警告:
error C2668: 'foo' : ambiguous call to overloaded function
.\xxxx.cpp(16): could be 'void foo(unsigned int)'
.\xxxx.cpp(15): or 'void foo(unsigned char)'
while trying to match the argument list '(size_t)'
这是因为 strlen 返回 size_t. 在 32-bit 系统中, size_t 恰好是 32-bit, 然而一个 unsigned int 也是 32-bit; 但是在 64-bit 中, size_t 变成了 64-bit, unsigned int 依然保留是 32-bit. 因此 foo 无法选择多态函数, 导致编译失败.
2. size_t 和 ptrdiff_t 是我们很常用, 且在 64-bit 下最常导致问题的两个类型.
size_t: is a C/C++ base unsigned integer type. It is the type of the result returned by sizeof operator. The size of the type is chosen so that it could store the maximum size of a theoretically possible array of any type. This type will be 32-bit on a 32-bit system and 64-bit on a 64-bit one.
ptrdiff_t: is a C/C++ base signed integer type. Its size is chosen so that it could store the maximum size of a theoretically possible array of any type. This type will be 32-bit on a 32-bit system and 64-bit on a 64-bit one.
3. size_t 和 ptrdiff_t 也被称作 memsize-types.
The term "memsize" appeared as an attempt to briefly name all the types that can store sizes of pointers or indexes of the largest arrays.
4. /Wp64 编译选项能够在 32-bit 开发时, 预测将来向 64-bit 移植可能会造成的问题的能力. 如下是一种大家熟悉的可移植模式:
typedef int MyInt32;
#ifdef _WIN64
typedef __int64 MySSizet;
#else
typedef int MySSizet;
#endif
void foo() {
MyInt32 value32 = 10;
MySSizet size = 20;
value32 = size;
}
但 value32 = size 这句话在真实的 64-bit 模式下将会发生截断, 然而在开发时并没有任何警告, 这就为今后移植重编译带来了麻烦, 因此, 我们有了 __w64 关键字:
typedef int MyInt32;
#ifdef _WIN64
typedef __int64 MySSizet;
#else
typedef int __w64 MySSizet; // Add __w64 keyword
#endif
void foo() {
MyInt32 value32 = 10;
MySSizet size = 20;
value32 = size; // C4244 64-bit int assigned to 32-bit int
}
这样在 32-bit 开发的时候, 我们就会检测到未来向 64-bit 移植时可能产生的麻烦.
Lesson 7. The issues of detecting 64-bit errors
Lesson 8. Static analysis for detecting 64-bit errors
Lesson 9. Pattern 1. Magic numbers
1. 在程序中不要使用 Magic Number, 即使它是为人熟悉的某些表示位数和数值上线的数值, 因为不同平台其值很可能不同. 应该尽量用 sizeof 操作符和 limits.h 代替它们.
比如这样的代码:
1) size_t ArraySize = N * 4;
intptr_t *Array = (intptr_t *)malloc(ArraySize);
2) size_t values[ARRAY_SIZE];
memset(values, 0, ARRAY_SIZE * 4);
3) size_t n, r;
n = n >> (32 - r);
就是不可移植的, 我们应该修改它为:
1) size_t ArraySize = N * sizeof(intptr_t);
intptr_t *Array = (intptr_t *)malloc(ArraySize);
2) size_t values[ARRAY_SIZE];
memset(values, 0, ARRAY_SIZE * sizeof(size_t));
2. 有时你可能需要只保留某个变量的最低 4 位, 那么如下的操作也是不可移植的:
// constant '1111..110000'
const size_t M = 0xFFFFFFF0u;
我们应该这样做:
#ifdef _WIN64
#define CONST3264(a) (a##i64)
#else
#define CONST3264(a) (a)
#endif
const size_t M = ~CONST3264(0xFu);
才是唯一的选择. (但是笔者认为, const size_t M = (size_t)-1 ^ 0x0F; 也可以达到同样的效果, 当然还有很多其它的技巧, 请参考: http://graphics.stanford.edu/~seander/bithacks.html)
3. 对于错误返回值, 通常选择 -1. 在 32-bit 模式下, 其值是 0xFFFFFFFF, 然而在 64-bit 模式下为 0xFFFFFFFFFFFFFFFF. 我们来看一个从 3D 模型中发现的真实代码:
hFileMapping = CreateFileMapping (
(HANDLE) 0xFFFFFFFF,
NULL,
PAGE_READWRITE,
(DWORD) 0,
(DWORD) (szBufIm),
(LPCTSTR) &FileShareNameMap[0]);
一眼就可以发现问题, (HANDLE) 0xFFFFFFFF 是一个容易引发错误的根源. 或许你会使用:
#define INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1)
来实现返回错误代码, 但是在 64-bit 系统中, 它是 0x00000000FFFFFFFF. 这也许不是你的预期.
4. 很显然, 错误的根源在于 int 类型在 32-bit 和 64-bit 平台的不同导致的. 让我们用一个简单的程序验证:
void foo(void *ptr)
{
cout << ptr << endl;
}
int _tmain(int, _TCHAR *[])
{
cout << "-1\t\t";
foo((void *)-1);
cout << "0xFFFFFFFF\t";
foo((void *)0xFFFFFFFF);
}
在 32-bit 模式下的结果是:
-1 FFFFFFFF
0xFFFFFFFF FFFFFFFF
在 64-bit 模式下的结果是:
-1 FFFFFFFFFFFFFFFF
0xFFFFFFFF 00000000FFFFFFFF
Lesson 10. Pattern 2. Functions with variable number of arguments
1. 以下代码是不可移植的:
const char *invalidFormat = "%u";
size_t value = SIZE_MAX;
printf(invalidFormat, value);
我们应该在 32-bit 模式开发时, 尽量的想到未来向 64-bit 移植时可能造成的问题. 因此上述 代码中的 %u 就会成为问题的根源.
在 64-bit Windows 中我们应该:
size_t s = 1;
printf("%Iu", s);
在 64-bit Linux 中, 我们应该:
size_t s = 1;
printf("%zu", s);
2. 在 64-bit 系统中, memsize-types 值作为变量长度的参数是危险的.
Lesson 11. Pattern 3. Shift operations
1. 在 64-bit 系统上操作位运算, 是十分容易造成错误的:
ptrdiff_t SetBitN(ptrdiff_t value, unsigned bitNum) {
ptrdiff_t mask = 1 << bitNum;
return value | mask;
}
这段代码在 32-bit 系统下会很好的工作. 然而在 64-bit 系统中, mask 的 32-64 位永远都会是 0. 因为 1 将作为 int 型变量参与左移运算, 因此当 bitNum 大于 31 时, int 型变量产生溢出, 导致左移运算结果为零. 修正的代码如下:
ptrdiff_t mask = ptrdiff_t(1) << bitNum;
2. 将一些常见的位移结果列表如下:
![](http://image50.360doc.cn/DownloadImg/2012/03/2316/22561259_1.png)
3. 以下又是个隐晦的错误:
struct BitFieldStruct {
unsigned short a:15;
unsigned short b:13;
};
BitFieldStruct obj;
obj.a = 0x4000;
size_t addr = obj.a << 17; //Sign Extension
printf("addr 0x%Ix\n", addr);
//Output on 32-bit system: 0x80000000
//Output on 64-bit system: 0xffffffff80000000
在 32-bit 系统下如图:
![](http://image50.360doc.cn/DownloadImg/2012/03/2316/22561259_2.png)
但是在 64-bit 下, 其解释如下:
![](http://image50.360doc.cn/DownloadImg/2012/03/2316/22561259_3.png)
Lesson 12. Pattern 4. Virtual functions
1. 引入著名框架 MFC 中的一个错误:
![](http://image50.360doc.cn/DownloadImg/2012/03/2316/22561259_4.png)
如图, 在基类 CWinApp 中, 有一个虚函数 WinHelp 其第一个参数为 DWORD 型变量, 然而在很有以前的 Visual Studio 6.0 中, 有一个派生类 CSampleApp, 实现了该虚函数.
然而到了 Visual Stutio 2005, CWinApp 中的 WinHelp 原型发生了变化:
![](http://image50.360doc.cn/DownloadImg/2012/03/2316/22561259_5.png)
在 32-bit 模式下它能够正常的工作. 因为 DWORD 和 DWORD_PTR 都是 32-bit 带符号整型. 但是到了 64-bit 系统上, 它将会是一个发生错误. 这不仅仅存在于 vs 2005 中, 有些第三方库也会包含这个错误.
Lesson 13. Pattern 5. Address arithmetic
1. 指针运算时向 64-bit 移植最容易造成错误的问题之一. 为了避免之, 我们要记住一点就是在地址运算时始终使用 memsize-types.
unsigned short a16, b16, c16;
char *pointer;
...
pointer += a16 * b16 * c16;
上述代码在 64-bit 系统下会引起微妙的错误. 首先, a16, b16, c16 都是 short 变量, 根据 C++ 规定, 这些变量只有转换为 int 型变量才能做乘法. 因此:
a16 * b16 * c16;
的结果是 int 型. 显然, 当其值超过 int 能够表示的最大值时会发生溢出. 这个溢出了的值最终会与一个地址进行计算而没有一点警告.
2. 如果你认为上述例子不会发生在你身上, 那么请看下面的代码:
1 int A = -2;
2 unsigned B = 1;
3 int array[5] = { 1, 2, 3, 4, 5 };
4 int *ptr = array + 3;
5 ptr = ptr + (A + B); //Invalid pointer value on 64-bit platform
6 printf("%i\n", *ptr); //Access violation on 64-bit platform
- 第三行: 根据 C++ 规则, 变量 A 需转换为 unsigned 才能与 B 进行加法
- A + B 的结果是 unsigned int 0xFFFFFFFF.
- 在 32-bit 系统中, ptr + 0xFFFFFFFF == ptr - 1
- 在 64-bit 系统中, ptr + 0xFFFFFFFF 则会实际加上 0xFFFFFFFF. 因为 64-bit 系统的指针都为 64 位. 这就引发了一个很可能不是你想要的结果.
所以, 我们在地址运算时, 应该始终使用 memsize-type 变量, 上述代码改为:
ptrdiff_t A = -2;
size_t B = 1;
ptr = ptr + (ptrdiff_t(A) + ptrdiff_t(B));
...
ptr = ptr + (A + B);
3. 对本 lesson 所属代码的总结就是, 两个 32-bit 变量运算会产生 32-bit 的结果, 无论是 signed 或者 unsigned, 当 32-bit 结果被赋值到 64-bit 变量中时, 会引发截断或者不必要的 sign extension. 得到的值往往不是我们想要的.
Lesson 14. Pattern 6. Changing an array's type
Lesson 15. Pattern 7. Pointer packing
1. 不要因为 64-bit 的到来, 就将指针固定为 64-bit, 因为 Microsoft 的研究人员已经开始提供 Windows 8 和 Windows 9 的 128-bit 架构. 因此, 始终使用 memsize-type 变量参与指针运算才是良好的编程风格.
Lesson 16. Pattern 8. Memsize-types in unions
1. 在 union 中, 把指针类型和 int 型放在一起是一个容易出错的情况. 就像下面的代码:
union PtrNumUnion {
char *m_p;
unsigned m_n;
} u;
u.m_p = str;
u.m_n += delta;
最后一句有可能会引起加法的溢出从而导致 u.m_n 错误.
Lesson 17. Pattern 9. Mixed arithmetic
1. 在数学运算中, 将 memsize-type 和非 memsize-type 混合运算, 将是一场灾难:
int x = 100000;
int y = 100000;
int z = 100000;
ptrdiff_t size = 1; // Result:
ptrdiff_t v1 = x * y * z; // -1530494976
ptrdiff_t v2 = ptrdiff_t (x) * y * z; // 1000000000000000
ptrdiff_t v3 = x * y * ptrdiff_t (z); // 141006540800000
ptrdiff_t v4 = size * x * y * z; // 1000000000000000
ptrdiff_t v5 = x * y * z * size; // -1530494976
ptrdiff_t v6 = size * (x * y * z); // -1530494976
ptrdiff_t v7 = size * (x * y) * z; // 141006540800000
ptrdiff_t v8 = ((size * x) * y) * z; // 1000000000000000
ptrdiff_t v9 = size * (x * (y * z)); // -1530494976
如同 lesson 13 中的例子那样, 上述代码大多都是因为溢出而发生截断, 导致了在 64-bit 系统上结果完全错误.
2. 有些 "聪明" 的人会使用 type casting 将其中的一个运算元提升至 memsize-type, 就像这样:
int c();
int d();
int a, b;
ptrdiff_t v2 = ptrdiff_t (a) * b * c() * d();
事实上, 没有标准规定运算的顺序是如何进行的, 因此可能 b * c() 会最先进行, 那么依然有可能发生溢出.
3. 在 memsize-type 和非 memsize-type 之间使用比较运算符也是非常危险的:
ptrdiff_t val_1 = -1;
unsigned int val_2 = 1;
if (val_1 > val_2)
printf ("val_1 is greater than val_2\n");
else
printf ("val_1 is not greater than val_2\n");
//Output on 32-bit system: "val_1 is greater than val_2"
//Output on 64-bit system: "val_1 is not greater than val_2"
根据 C++ 规定, 在 32-bit 系统中 val_1 会扩展为 unsigned int 0xFFFFFFFFu 并于 val_2 做比较, 因此 0xFFFFFFFFu > 0x00000001. 但是在 64-bit 系统中, val_2 会被扩展为 ptrdiff_t, 因此 -1 > 1 将会被正确的判断.
32-bit 系统下判定图示:
![](http://pubimage.360doc.com/wz/default.gif)
64-bit 系统下判定图示:
![](http://pubimage.360doc.com/wz/default.gif)
当然, 使用 signed 类型和 unsigned 类型进行比较本身就是一个严重的错误.
Lesson 18. Pattern 10. Storage of integer values in double
Lesson 19. Pattern 11. Serialization and data interchange
1. 不要在二进制接口中使用可变长度类型:
size_t PixelsCount;
fread(&PixelsCount, sizeof(PixelsCount), 1, inFile);
我们应该修正代码:
size_t PixelsCount;
__uint32 tmp;
fread(&tmp, sizeof(tmp), 1, inFile);
PixelsCount = static_cast<size_t>(tmp);
这就是为什么很多 programmer 严格的使用那些有固定长度的类型, 如 __int8, __int16, __int32, 以及 word64 等.
2. 字节序多个字节变量在内存中存放的次序规则.
- little-endian 意为最低位字节放在最前面, 最高位字节放在后面. x86 和 x86-64 采用了这种方式.
- big-endian 意为最高位放在最前面, 最低为放在后面. TCP/IP 标准采用了这种协议, 这也是为什么 big-endian 也被称为网络序的原因. Motorola 68000 和 SPARC 处理器也采用了这种协议.
![](http://pubimage.360doc.com/wz/default.gif)
3. 在二进制接口开发时, 应该时刻记住字节序问题. 如果在 64-bit 系统中运行的 32-bit 辅助程序和你的程序字节序不同, 那么你就需要在程序中进行调整. 将 big-endian 与 little-endian 相互调整可以使用 htonl(), htons(), bswap_64 等. 有些系统可能没有 bswap_64, 那么如何处理 64-bit 类型呢? 可参考: 64 bit ntohl() in C++ ?
Lesson 20. Pattern 12. Exceptions
1. 在异常机制中, 好的编程风格是所有抛出的异常都应继承自标准异常类, 如 std::exception.
2. 涉及指针减法的异常, 绝不可用 int 作为其捕获条件.
char *ptr1;
char *ptr2;
try {
try {
throw ptr2 - ptr1;
}
catch (int) {
std::cout << "catch 1: on x86" << std::endl;
}
}
catch (ptrdiff_t) {
std::cout << "catch 2: on x64" << std::endl;
}
Lesson 21. Pattern 13. Data alignment
1. 展示一段有问题的代码:
struct MyPointersArray {
DWORD m_n;
PVOID m_arr[1];
} object;
...
malloc( sizeof(DWORD) + 5 * sizeof(PVOID) );
...
其内存分布如图:
![](http://pubimage.360doc.com/wz/default.gif)
显然, 在 64-bit 下程序段不能正确的工作.
2. Linux 程序员会遇到更多关于 memory alignment 的问题. 详情请看: Change of type alignment and the consequences
Lesson 22. Pattern 14. Overloaded functions
1. 由于 64-bit 系统中 memsize-type 长度的变化, 会引发一些可能不期望的函数重载.
static size_t GetBitCount(const unsigned __int32 &) {
return 32;
}
static size_t GetBitCount(const unsigned __int64 &) {
return 64;
}
size_t a;
size_t bitCount = GetBitCount(a);
Lesson 23. Pattern 15. Growth of structures' sizes
1. C++ 结构体会随着向 64-bit 系统的迁移而变大. 正如:
struct MyStruct
{
bool m_bool;
char *m_pointer;
int m_int;
};
在 32-bit 系统中是 16, 然而到了 64-bit 系统中则是 24.
2. 结构体的 memory-alignment 简述:
In general, the alignment rule is as follows: each field is aligned on the address multiple of the size of this field. A field of size_t type on a 64-bit system will be aligned on an 8-byte boundary, int on a 4-byte boundary, short on a 2-byte boundary. Fields of char type are not aligned. The size of such a structure is aligned on the size multiple of the size of its maximum item.
3. 我们对于结构体的布局, 有一个简单的优化方法: 将最大长度类型的变量放在前面, 将最小类型的变量放在后面.
1. 我们都知道在地址运算中使用 int 或者 unsigned int 会造成难以察觉的问题, 那么请看下面的代码:
int index = 0;
size_t arraySize = ...;
for (size_t i = 0; i != arraySize; i++)
array[index++] = BYTE(i);
理论上 index 会因为溢出而导致在 64-bit 系统上的运行错误, 但是当 index 超过 INT_MAX 时程序依然正确, 我们检查其汇编结果:
0000000140001040 mov byte ptr [rcx+rax],cl
0000000140001043 add rcx,1
0000000140001047 cmp rcx,rbx
000000014000104A jne wmain+40h (140001040h)
发现, 由于编译器的优化, 导致程序看上去 "正常". 那么我们将程序修改成如下:
int index = 0;
size_t arraySize = ...;
for (size_t i = 0; i != arraySize; i++)
{
array[index] = BYTE(index);
++index;
}
再次观察其汇编结果:
0000000140001040 movsxd rcx,r8d
0000000140001043 mov byte ptr [rcx+rbx],r8b
0000000140001047 add r8d,1
000000014000104B sub rax,1
000000014000104F jne wmain+40h (140001040h)
当 index 超过 INT_MAX 时程序会导致崩溃, 因为 rcx 被扩展为 0xffffffff80000000.
更多类似的例子, 请看原文链接.
Lesson 25. Working with patterns of 64-bit errors in practice
1. 64-bit 模式下编译应用程序会增加一定的的内存消费. 其运行时内存开销甚至有可能是两倍于 32-bit 模式下编译的应用程序. 增长的内存开销主要源于一下三个方面:
- 存储对象需更大的内存空间, 如指针.
- 结构体的 memory-align 规则导致其长度变大
- 栈增长的开销
我们不必太过拘泥于这些内存的增长, 就像 300MB 在 2G 的内存空间中和 400MB 在 8 G 的内存空间中没有任何影响一样.
2. 在指针运算中使用 ptrdiff_t 和 size_t, 不单为了其安全性, 也是因为在 64-bit 系统中使用 int 进行地址运算会在二进制代码层次上增加额外的开销, 影响性能. 事实上, 使用 size_t 会使编译器有更多的优化:
unsigned arraySize;
...
for (unsigned i = 0; i < arraySize / 2; i++)
{
float value = array[i];
array[i] = array[arraySize - i - 1];
array[arraySize - i - 1] = value;
}
![](http://pubimage.360doc.com/wz/default.gif)
Lesson 27. Peculiarities of creating installers for a 64-bit environment
1. 以下是创建 64-bit installer 时需要注意的几方面:
- installer 应该技能工作在 64-bit 也能工作在 32-bit, 否则你创建一个 64-bit 的 installer 会导致 32-bit 系统上无法安装的消息 --- "You are trying to install a distribution kit of a 64-bit program on a 32-bit system"
- 在 Windows-64 中需要注意的就是 redirection 的问题. 32-bit 应用程序会被安装在如 "c:\program files (x86)" 下. 此外, 在这册表中, 32-bit 应用程序都在 HKEY_LOCAL_MACHINE\Software\Wow6432Node 下.
- 最后, 不要忘记随着程序一起, 分别发行程序会用到的相应版本的库. 32-bit 和 64-bit 的库不能相互使用.
更多向 64-bit 移植的问题, 请看: http://msdn.microsoft.com/en-us/library/bb427430(v=VS.85).aspx
Lesson 28. Estimating the cost of 64-bit migration of C/C++ applications