12可移植性(相容性)

來源:互聯網
上載者:User

12可移植性(相容性)

本章討論C++可移植性問題主要關註:32位移植到64位,不同CPU架構之間的移植。

移植中一些關鍵問題如下:

1.  指標截斷

2. 資料類型位元組對齊

3. 對記憶體位址的錯誤假設

4. 對複合資料型別成員地址的錯誤假設

5. 大小端,網路位元組序問題
建議12.1 不直接使用C++的基礎資料型別 (Elementary Data Type),不要假定其儲存尺寸長度

說明:C++標準沒有明確基礎資料型別 (Elementary Data Type)的大小與儲存格式,這些基本類型包括:short,int, long, long

long, float double等。這些基礎資料型別 (Elementary Data Type)在不同的編譯器中,實現有所不同,如:

long類型在32位編譯模式下為4位元組長度,在64位編譯模式下為8位元組長度。

所以建議不要直接使用基礎資料型別 (Elementary Data Type)。推薦如下兩種使用方式:

1、重定義基礎資料型別 (Elementary Data Type)

    typedef int32_t int;
    typedef int64_t long long;

使用重定義後的基本類型好處是:如果程式需要移植,可以大大減少移植的工作量。

2、使用C99標準中定義的標準類型

    int64_t my_value = 0x123456789LL;
    uint64_t my_mask = 3ULL << 48;
使用這些標準類型長度的好處是,它們規定了固定的長度,這個長度不會隨編譯器變化而變化,所以

我們可以放心的使用。

建議12.2 避免指標截斷

說明:指標截斷是從32位移植到64位系統時,經常會碰到的問題。

    int *i = &int_val;
    short *w = (short*)((int)i + 2);
上面的代碼在32位環境下運行是沒有問題的,但在64位環境下,發生了地址截斷:無法把64位長的數

據接到32位的資料空間裡面。

上面的代碼中可以使用intptr_t類型解決:

    int *i = &int_val;
    short *w = (short*)((intptr_t)i + 2);

建議12.3 注意資料類型對齊問題

說明:需要對結構對齊加以留心,尤其是對於儲存在磁碟上的結構體。

在64位系統中,任何擁有int64_t/uint64_t成員的類/結構體將預設被處理為8位元組對齊。如果32位和64                   位

代碼共用磁碟上的結構體,需要確保兩種體繫結構下的結構體的一致對齊。

另外,大多數編譯器提供了調整結構體對齊的方案:

gcc 中可使用__attribute__((packed)),MSVC 提供了#pragma pack()和__declspec(align()) 。

由於各個平台和編譯器的不同,所以在不同編譯器與平台移植代碼時,一定要特別關注編譯器關於對

齊的參數設定與預設值。因為位元組對齊不僅影響效能,而且會導致一些不可預知的問題。

建議12.4 在涉及網路位元組序處理時,要注意進行網路位元組序與本地位元組序的轉換

說明:小端法(Little-Endian)

低位位元組排放在記憶體的低地址端即起始地址,高位位元組排放在記憶體的高地址端。

大端法(Big-Endian)

高位位元組排放在記憶體的低地址端即起始地址,低位位元組排放在記憶體的高地址端。
不同cpu平台上位元組序通常也不一樣:

X86、AMD64平台使用小端法、而HP-IA, IBM AIX的CPU採用的是大端法。

而網路位元組序是大端法,如常見網路發送的碼流。

涉及網路位元組需要注意處理網路位元組序與本地位元組序的轉換,即使本地位元組序採用的也是大端法,為

了程式可移植性,建議也調用轉換函式進行轉換。

庫函數提供了16,32位整型int的網路位元組序與本地位元組序的轉換函式:

    htonl, htons, ntohl, ntohs - convert values between host and network byte order

建議12.5 避免無符號數與有符號數的轉換

說明:不同的國際標準(ANSI   C/ISO  C++等)對隱式轉換有符號和無符號類型的規則不同,有可能導致

不同的執行結果。

    unsigned short usNumber = xxx;
    long lNum = usNumber;
將unsigned   short賦值給long需要經過兩次類型轉換,ANSI標準中沒有規定多次類型轉換的順序。大多

數編譯器(例如VC)在高位優先填充0,按照下面的順序進行轉換:

      lNum = (long) (unsigned long) usNumber;

個別編譯器(例如BSD的一些編譯器)在高位優先填充1,即使用下面的順序進行轉換:

      lNum = (long) (signed short) usNumber;

如果是後一種轉換順序,並且正好usNumber 的高位為1,則首先被轉換成一個負數的long,接著轉換成

unsigned long時就成了很大的數。

usNumber永遠不可能為負數,沒有必要使用signed修飾。

修改辦法是定義lNum的類型為unsigned long,並更改名字為ulNum:

    unsigned short usNumber = xxx;
    unsigned long ulNum = usNumber;
盡量避免無符號數與有符號數的轉換,特別是長度不同數值的類型轉換。請首先考慮設計上是否需要

這種轉換。

建議12.6 建立64 位元常量時使用LL 或ULL 作為尾碼

說明:指定LL或ULL尾碼說明,能讓代碼更加清晰。

    int64_t my_value = 0x123456789LL;
    uint64_t my_mask = 3ULL << 48;
尤其是在>>操作時,無符號與有符號有很大差異的,如果運算元是無符號數,則右移操作符>>,從左

邊開始插入0, 否則插入符號位的拷貝或者插入0                ,這由編譯器決定。

建議12.7 區分sizeof(void *)和sizeof(int)

說明:64位下sizeof(void *) != sizeof(int),而在32位下是相同的。

如果需要一個指標大小的整數請使用intptr_t。

建議12.8 編譯器,作業系統相關的代碼獨立出來

說明:為了程式的可移植性,建議將編譯器,作業系統相關的代碼從產品代碼中獨立出來。

編譯器特有的東西,如gcc的編譯參數__thread,            __attribute__等,如果需要做到支援多個平台,需

要封裝為宏或函數。
例如gcc的__thread對應的VC開關為__declspec(thread)

    __thread int number;
    __declspec(thread) int number;
均表示number為線程私人儲存變數,可採用如下宏來封裝:

    #ifdef WIN32
    #define THREAD __declspec(thread)
    #else
    #define THREAD __thread
    #endif

    THREAD int number;

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.