2013-02-21 114 views
5

一个C++的具体问题。所以我读了一个关于什么使得程序是32位/ 64位的问题,它得到的anwser是这样的(对不起,我找不到问题,有些日子之前我看了它,我不能再次找到它:():只要你不做任何“指针假设”,你只需要重新编译它。所以我的问题是,什么是指针假设?据我的理解,有32位指针和64位指针,所以我认为这是与请在代码间显示不同的代码,在编写代码时要记住任何其他好习惯,这有助于使代码间的转换变得容易:)请与他们分享示例如何编写32位/ 64位可转换代码?

Ps 。我知道有这个职位: How do you write code that is both 32 bit and 64 bit compatible? 但我认为它是一种通常没有很好的例子,像我这样的新程序员。像什么是32位存储单元等。有点跳跃打破它多一点(没有双关意图^ ^)DS。

回答

3

对于格式良好的程序(即,根据C++的语法和语义规则编写的程序,没有未定义的行为),C++标准保证您的程序将具有一组可观察行为之一。可观察到的行为因程序中的未指定行为(包括实现定义的行为)而异。如果您避免未指定的行为或解决它,您的程序将保证具有特定的确定输出。如果你用这种方式编写你的程序,你将在32位或64位机器上见证你的程序没有区别。

一个程序,将具有不同的可能的输出的一个简单的(强制)例子如下:

int main() 
{ 
    std::cout << sizeof(void*) << std::endl; 
    return 0; 
} 

这一方案将可能在32位和64位机器的不同输出(但不一定)。 sizeof(void*)的结果是实现定义的。然而,可以肯定的是能够有一个包含实现定义的行为,但决心是明确的程序:

int main() 
{ 
    int size = sizeof(void*); 
    if (size != 4) { 
    size = 4; 
    } 
    std::cout << size << std::endl; 
    return 0; 
} 

这一计划将始终打印出4,尽管它使用实现定义的行为。这是一个愚蠢的例子,因为我们可以刚刚完成int size = 4;,但有些情况下,在编写与平台无关的代码时出现这种情况。

因此,编写便携式代码的规则是:旨在避免或解决未指定的行为

下面是避免不确定的行为的一些提示:

  1. 不要以为对基本类型的大小超出了其C++标准规定什么。也就是说,char至少是8位,shortint都是至少16位,依此类推。

  2. 不要尝试做指针魔术(在指针类型之间进行转换或将指针存储在整型中)。

  3. 请勿使用unsigned char*来读取非对象(用于序列化或相关任务)的值表示形式。

  4. 避免reinterpret_cast

  5. 执行操作可能超过或下溢时要小心。在进行移位操作时请仔细考虑。

  6. 上指针类型做算术时要小心。

  7. 不要使用void*

标准中还有更多未指定或未定义的行为出现。这非常值得一看。网上有some great articles,涵盖了32位和64位平台之间会遇到的一些更常见的差异。

+0

awsome anwser :)只是我在找:) – 2013-02-21 13:12:33

+0

@FredrikBostonWestman那么你应该接受它! – gsamaras 2015-04-04 15:41:49

3

“指针假设”是指编写依赖于其他数据类型的指针的代码,例如, int copy_of_pointer = ptr; - 如果int是32位类型,那么这个代码将在64位机器上中断,因为只有部分指针会被存储。

只要指针只存储在指针类型中,它应该没有问题。

通常,指针是“机器字”的大小,所以在32位架构,32位和64位架构上,所有指针都是64位。但是,有些架构并非如此。我自己从来没有在这类机器上工作(除x86外,它的“远”和“近”指针 - 但现在让我们忽略)。

大多数编译器会告诉你什么时候把指针转换成指针不适合的整数,所以如果你启用了警告,那么大多数的问题将会变得明显 - 修复警告,并且机会相当不错,你的代码将立即工作。

1

32位代码和64位代码没有区别,C/C++和其他编程语言的目标是它们的可移植性,而不是汇编语言。

唯一的区别是你将编译你的代码的distrib,所有的工作都是由你的编译器/链接器自动完成的,所以不要去想那个。但是:如果您在64位distrib上编程,并且您需要使用外部库(例如SDL),那么如果您希望编译代码,则外部库也必须以64位编译。

要知道的一件事是,你的ELF文件在64位分配上比在32位分配上要大,这只是逻辑。

指针有什么用?当您增加/更改指针时,编译器会将指针从指向类型的大小增加。

包含的类型大小由您的处理器的寄存器大小/处理的distrib来定义。

但是你只是不必关心这一点,汇编将为你做所有事情。

总结:这就是为什么你不能在32位distrib上执行64位ELF文件的原因。

+0

当你说你打扰你吗? – 2013-02-21 12:09:32

+1

@FredrikBostonWestman:这个答案假设一个现代的基于Linux/BSD的操作系统。这种操作系统的“实现”称为分发;它们被设计成一个稳定的完整包,其中的一切都是专门为它将运行的体系结构编译的。用闭源技术很难获得这样的好处,显然... – leftaroundabout 2013-02-21 12:15:47

6

一般而言,这意味着您的程序行为不应该取决于任何类型的sizeof()(不是明确地或非明确地)(包括可能的结构对齐)。

指针只是其中的一个子集,它可能还意味着,你不应该试图依靠的是能够提供给无关的指针类型和/或整数之间的转换,除非它们是专为这一目的(例如intptr_t)制成。以同样的方式,你需要照顾写入磁盘的东西,在这种情况下,你也不应该依赖例如磁盘的大小。内置类型,无处不在。

无论何时您必须(因为例如外部数据格式)使用明确大小的类型,如uint32_t

1

为32位/ 64位移植的典型缺陷是:

由程序员那的sizeof(无效*)== 4 *的sizeof(char)的隐含的假设。 如果你正在做这个假设,例如以这种方式分配数组(“我需要20个指针,因此我分配80个字节”),您的代码在64位上断开,因为它会导致缓冲区溢出。

“小猫杀手”,int x =(int)&something; (并且相反,void * ptr =(void *)some_int)。再次假设sizeof(int)== sizeof(void *)。这不会导致溢出,但会丢失数据 - 指针的高32位,即。

这两个问题是一类称为混叠型(两种类型之间的二进制表示水平假设身份/互换性/等效)的,并且这样的假设是常见的;像在UN * X上一样,假设time_t,size_t,off_t是int,或者在Windows上,HANDLE,void *和long可互换等等。

关于数据结构/堆栈空间使用的假设好)。在C/C++代码中,局部变量分配在堆栈上,由于下面的点,因此在32位和64位模式之间使用的空间不同,并且由于传递参数的不同规则(通常在堆栈上有32位x86,64位x86部分在寄存器中)。刚刚摆脱32位默认堆栈大小的代码可能会导致64位堆栈溢出崩溃。 这是相当容易发现作为崩溃的原因,但取决于可能难以解决的应用程序的可配置性。 32位和64位代码之间(由于不同的代码尺寸/高速缓存足迹,或不同的存储器访问特征/图案,或不同的调用约定)

时间差异可能会破坏“校准”。说,for(int i = 0; i < 1000000; ++ i)sleep(0);很可能将会有不同的时间对32位和64位...

最后,ABI(应用程序二进制接口)。通常,64位环境中存在两个主要的“分支”,IL32P64(Win64使用的是--int和long是int32_t,只有uintptr_t/void *是uint64_t,根据来自)和LP64(UN * X使用的是什么 - int是int32_t,long是int64_t和uintptr_t/void *是uint64_t),但也存在不同对齐规则的“细分” - 一些环境假设长,浮动或双对齐,而其他人则假定它们以四个字节的倍数对齐。在32位Linux中,它们全部以四个字节对齐,而在64位Linux中,它们以八字节倍数浮动对齐四位,长两位。 这些规则的结果是,即使数据类型声明完全相同,在很多情况下,bith sizeof(struct {...})和结构/类成员的偏移量在32位和64位环境之间也是不同的。 除了影响阵列/向量分配之外,这些问题还会影响数据输入/输出。通过文件 - 如果32位应用程序编写例如struct {char a; int b; char c,long d;双e}到同一个应用程序重新编译为64位的文件读入,结果将不会完全符合所希望的。 刚刚给出的例子只是关于语言原语(char,int,long等),但当然会影响各种平台相关/运行时库数据类型,无论size_t,off_t,time_t,HANDLE,本质上是任何非平凡的结构/联合/ class ... - 所以这里的错误空间很大,

然后还有更低层次的差异,用于手动优化组装(SSE/SSE2/...); 32位和64位具有不同的(数量)寄存器,不同的参数传递规则;所有这些都强烈地影响着这种优化如何执行,并且很可能如在32位模式下提供最佳性能的SSE2代码将需要重写/需要增强以提供最佳性能的64位模式。对于32位和64位,还有代码设计约束条件非常不同,尤其是在内存分配/管理方面;一个经过仔细编码的应用程序“最大限度地利用mem可以获得32位”将具有复杂的逻辑,如何/何时分配/释放内存,内存映射文件使用率,内部缓存等 - 其中大部分将在64位上是不利的,你可以“简单地”利用巨大的可用地址空间。这样的应用程序可能会重新编译为64位就好了,但在那里执行比一些“古老的简单的不推荐的版本”,它没有所有的最大化 - 32位窥视孔优化。因此,最终它也是关于增强/增益的,这就是更多的工作,部分是编程,部分是设计/需求。即使你的应用在32位和64位环境中都干净地重新编译,并在两者上都得到验证,它实际上是从64位受益?是否可以/应该对代码逻辑进行更改,以使其在64位中执行更多/更快的运行?你能做这些改变而不破坏32位向后兼容性吗?对32位目标没有负面影响?增强功能在哪里,你能获得多少? 对于大型商业项目,这些问题的答案往往是路线图上的重要标记,因为您的出发点是一些现有的“货币制造商”...

+1

值得看看http://www.viva64.com/en/a/0004/ – Saqlain 2013-02-21 12:32:08