翻译《有关编程、重构及其他的终极问题?》——6.当把一个指针明确的转换为整型时,请检查所有相关代码

翻译《有关编程、重构及其他的终极问题?》——6.当把一个指针明确的转换为整型时,请检查所有相关代码

标签(空格分隔): 翻译 技术 C/C++
作者:Andrey Karpov
翻译者:顾笑群 - Rafael Gu
校验者:高国栋
最后更新:2016年12月02日


本书背景说明、总目录等介绍,可以跳转到以下链接进行查看:
http://blog.csdn.net/headman/article/details/53045891

欢迎大家转载,但请附上原作者以及翻译者的名字、原文出处,以尊重光荣的劳动者。


6.当把一个指针明确的转换为整型时,请检查所有相关代码

下面这段代码摘自IPP Samples项目。PVS-Studio诊断出来的错误为:V205 Explicit conversion of pointer type to 32-bit integer type: (unsigned long)(img)(译者注:大意是明确的把指针转换为32位整型了)

void write_output_image(...., const Ipp32f *img, 
                        ...., const Ipp32s iStep) {
  ...
  img = (Ipp32f*)((unsigned long)(img) + iStep);
  ...
}

注意:也许你们有些人因为一些原因说上面的代码不是最好的示例。但其实我们并不关注为什么一个程序员会用那么奇怪的方式移动一段数据,我们关注的只是指针明确的转换为unsigned long类型,而且这段代码看上去比较简单。

解释
程序员们(译者注:其实应该是经典的原生编程程序员)往往把指针视作若干字节表示。上面的代码就是如此,但只能在Win32系统上工作,因为其上的指针和long类型的字节数是一样的。但是当我们在64位(Windows)平台上编译这段代码后,指针就会变成64位,转换为long类型(译者注:注意,即使在64位系统上,long类型在部分平台上也是32位长度,long long类型才是64位长度,见下图)时,就会损失高位字节。
屏幕快照 2016-12-06 20.08.15.png-43.3kB

注意,Linux(和Windows比)用了不同的data model。在64位Linux程序里,(译者注:其实64位平台上的部分类型的长度主要和编译器相关,而非直接和操作系统相关)long类型是64位长度,即使如此,使用long类型存储指针也不是一个好主意。第一,类似代码会经常在Windows上编译成应用,那样的话,这就是错误的;第二,一般系统都会提供通过名字就能看出来的类型来存储指针,比如intptr_t,使用这些类型可以让程序更清晰。

在上面的例子中,我们看到只是在64位程序中发生的一个经典错误。其实在64位程序的开发过程中,还有很多其他错误比较容易发生,但把一个指针转换为32位整型变量是最普遍、隐藏最深的问题。

这个错误可以用下图来说明:
image2.png-148kB
图一:A)32位程序指针指向对象。B)64位指针指向对象,但地址只有地位。C)64位指针损失数据了

之所以说这个问题是隐藏最深的,是因为它常常很难被注意。这样的程序一般是工作的(译者注:运气好的话),错误只有在程序经过频繁使用几个小时候后才会因为指针数据缺少才会暴露出来。因为开始内存被分配在低内存区,也就是第一个4GB空间(译者注:32位指针最大只能控制4GB空间),这个时候就没有任何问题,但之后(译者注:超过4GB空间)就难说了。

当程序一直保持运行,内存不断被释放和分配,即使程序没有使用多少内存,新的对象也很有可能在超过4GB空间的地方创建,这时问题就出现了。这种问题一般是不是规律重现的(有时在4GB空间内,有时在外面)。

正确的代码
建议使用size_t,INT_PTR,DWORD_PTR,intrptr_t等类型存储指针(译者注:其实我不建议用size_t)。

img = (Ipp32f*)((uintptr_t)(img) + iStep);

事实上,我们可以不用明确转换的方式就能实现指针的存储。在这里我们也没有看到任何地方提到默认的格式对齐的问题(译者注:不同指针的规范字节数是不一样的),这也是我们看不到显示的调用__declspec(align(#))等编译指令的原因,所以上面运算最后的字节数都会默认被Ipp32f的长度所除(译者注:隐式的格式对齐);否则会有不可预知的意外(详见EXP36-C)。

所以在了解指针字节对齐的原理后,我们可以像如下这样改编上述语句:

img += iStep / sizeof(*img);

建议
使用特定的类型去存储指针——忘掉int和long。最通用的存储指针的方式有:intptr_t和uintptr_t。在Visual C++中,这些类型可用:INT_PTR,UNIT_PTR,LONG_PTR,ULONG_PTR,DWORD_PTR。他们的名字也恰如其分的告诉你可以用来安全的保存指针。

当然一个指针也能存储在size_t和ptrdiff_t类型中,但我不建议用它们,因为他们原来分别是用来保存长度和下标的。

你不能把一个类的成员函数指针保存在uintptr_tr中。成员函数和一般的函数还有些细微的差别:除了指针本身,还需要保存指向对象实例的this指针。然后在32位程序中,反正你也不能把这样一个指针赋给unsigned int类型,所以不会产生问题。因为这类指针的特殊处理方式,所以在64位程序上问题也不大,至少我没有碰到过。

如果你要在64位平台上编译你的程序,你首先需要review和修改所有把指针转换成32位整型的地方。记住:你的程序(译者注:在32位转移到64位时)会有很多问题,但你应该从处理指针开始。

如果你有计划创建64位程序,我建议可以研究一下这个资源:Lessons on development of 64-bit C/C++ applications

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值