2013-02-21 69 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