linux驅動編寫32位與64位設備註意事項

數據類型問題

Linux系統32位與64位GCC編譯器基本數據類型長度對照表

GCC 32位
sizeof(char)=1
sizeof(double)=8
sizeof(float)=4
sizeof(int)=4
sizeof(short)=2
sizeof(long)=4
sizeof(long long)=8
sizeof(long double)=12
sizeof(complex long double)=24
GCC 64位
sizeof(char)=1
sizeof(double)=8
sizeof(float)=4
sizeof(int)=4
sizeof(short)=2
sizeof(long)=8
sizeof(long long)=8
sizeof(long double)=16
sizeof(complex long double)=32
 以下爲從網上獲取的信息 ,直接copy過來,以防丟失。

64位操作系統編碼規範

一、Linux及Windows主要類型字節長度

類型

32位

64位

short

2

2

int

4

4

long

4

Linux:8 Windows:4

size_t

4

8

void *(及其它指針)

4

8

time_t

8

8

float

4

4

double

8

8

 

二、不允許使用long型;

三、不允許把int強制轉換爲指針,反之亦然;下面是錯誤的:int i = (int )ptr; 或: void *ptr = (void *)s32Tmp;

四、不允許在不同類型的指針(有繼承關係除外)進行強制轉換,如:int *ipTmp; time_t *tpTmp = *ipTmp;是錯誤的;

五、強烈建議使用帶長度的類型,如:SInt32/UInt64等;

六、必須使用%p打印指針,如printf(“this[%p]\n”,this);

七、強烈建議不要採用scanf,scanf非常危險,Format和參數類型不一致時,非常容易導致越界寫。如果必須使用,則必須認真區別32和64位的操作系統整數長度的差別,需要時,分別針對32和64位編寫代碼;

八、如果編寫動態鏈接庫,必須加上–fPIC編譯選項;

九、採用GCC編譯器時,必須使用-Wlong-long、-Wformat -Wpointer-arith 來發現 64 位編譯問題,並關注相關警告信息;

十、編譯器指示宏定義

宏定義

描述

__linux__

Linux操作系統,與Windows及其它類型操作系統區別

i386

Linux操作系統,32位x86架構

x86-64(注意是減號)

Linux操作系統,64位x86架構

_WINDOWS_

Windows操作系統,與其它操作系統區別

_X86_

Windows操作系統,32位x86架構

__MacOSX__

Apple OSX

_posix_

符合POSIX規範,類Unix操作系統,包括Linux/IBM AIX/HPUS/Sun Solaris/Apple MacOSX等

十一、        宏使用說明:

l        我們的代碼要求同時支持32位和64位版本;

l        儘量不要使用與架構相關的宏,如i386/ x86-64/_X86_/Win32等,在同一操作系統中,API一般與架構無關,這樣可以讓代碼具有良好的移植性;

l        我們的軟件主要在類Unix(符合POSIX規範,包括Linux)和Windows下運行,優先使用_posix_作爲區別非windows操作系統;

l        如果對準備使用的類UNIX下系統調用(API)是否支持其它操作系統沒有把握,可使用_posix_加以標記,待日後移植到其它操作系統時,再測試或修改;


  Linux 64 位體系結構

    不幸的是,C 編程語言並沒有提供一種機制來添加新的基本數據類型。因此,提供 64 位的尋址和整數運算能力必須要修改現有數據類型的綁定或映射,或者向 C 語言中添加新的數據類型。

    表 1. 32 位和 64 位數據模型

  ILP32 LP64 LLP64 ILP64
char 8 8 8 8
short 16 16 16 16
int 32 32 32 64
long 32 64 32 64
long long 64 64 64 64
指針 32 64 64 64
    這 3 個 64 位模型(LP64、LLP64 和 ILP64)之間的區別在於非浮點數據類型。當一個或多個 C 數據類型的寬度從一種模型變換成另外一種模型時,應用程序可能會受到很多方面的影響。這些影響主要可以分爲兩類:

    數據對象的大小。編譯器按照自然邊界對數據類型進行對齊;換而言之,32 位的數據類型在 64 位系統上要按照 32 位邊界進行對齊,而 64 位的數據類型在 64 位系統上則要按照 64 位邊界進行對齊。這意味着諸如結構或聯合之類的數據對象的大小在 32 位和 64 位系統上是不同的。

    基本數據類型的大小。通常關於基本數據類型之間關係的假設在 64 位數據模型上都已經無效了。依賴於這些關係的應用程序在 64 位平臺上編譯也會失敗。例如,sizeof (int) = sizeof (long) = sizeof (pointer) 的假設對於 ILP32 數據模型有效,但是對於其他數據模型就無效了。

    總之,編譯器要按照自然邊界對數據類型進行對齊,這意味着編譯器會進行 “填充”,從而強制進行這種方式的對齊,就像是在 C 結構和聯合中所做的一樣。結構或聯合的成員是根據最寬的成員進行對齊的。清單 1 對這個結構進行了解釋。

    清單 1. C 結構


struct test {
	int i1;
	double d;
	int i2;
	long l;
}

    表 2 給出了這個結構中每個成員的大小,以及這個結構在 32 位系統和 64 位系統上的大小。

    表 2. 結構和結構成員的大小


結構成員 在 32 位系統上的大小 在 64 位系統上的大小
struct test {    
int i1; 32 位 32 位
    32 位填充
double d; 64 位 64 位
int i2; 32 位 32 位
    32 位填充
long l; 32 位 64 位
}; 結構大小爲 20 字節 結構大小爲 32 字節

    注意,在一個 32 位的系統上,編譯器可能並沒有對變量 d 進行對齊,儘管它是一個 64 位的對象,這是因爲硬件會將其當作兩個 32 位的對象進行處理。然而,64 位的系統會對 d 和 l 都進行對齊,這樣會添加兩個 4 字節的填充。

    從 32 位系統移植到 64 位系統

    本節介紹如何解決一些常見的問題:

    聲明表達式賦值數字常數Endianism類型定義位移字符串格式化函數參數

    聲明

    要想讓您的代碼在 32 位和 64 位系統上都可以工作,請注意以下有關聲明的用法:

    根據需要適當地使用 “L” 或 “U” 來聲明整型常量。

    確保使用無符號整數來防止符號擴展的問題。

    如果有些變量在這兩個平臺上都需要是 32 位的,請將其類型定義爲 int.如果有些變量在 32 位系統上是 32 位的,在 64 位系統上是 64 位的,請將其類型定義爲 long.爲了對齊和性能的需要,請將數字變量聲明爲 int 或 long 類型。不要試圖使用 char 或 short 類型來保存字節。

    將字符指針和字符字節聲明爲無符號類型的,這樣可以防止 8 位字符的符號擴展問題。

    表達式

    在 C/ 中,表達式是基於結合律、操作符的優先級和一組數學計算規則的。要想讓表達式在 32 位和 64 位系統上都可以正確工作,請注意以下規則:

    兩個有符號整數相加的結果是一個有符號整數。

    int 和 long 類型的兩個數相加,結果是一個 long 類型的數。

    如果一個操作數是無符號整數,另外一個操作數是有符號整數,那麼表達式的結果就是無符號整數。

    int 和 doubule 類型的兩個數相加,結果是一個 double 類型的數。此處 int 類型的數在執行加法運算之前轉換成 double 類型。

    賦值

    由於指針、int 和 long 在 64 位系統上大小不再相同了,因此根據這些變量是如何賦值和在應用程序中使用的,可能會出現問題。下面是有關賦值的一些技巧:

    不要使用 int 和 long 類型,因爲這可能會導致高位數字被截斷。例如,不要做下面的事情:

 

int i;
long l;
i = l;

    不要使用 int 類型來指針。下面這個例子在 32 位系統上可以很好地工作,但是在 64 位系統上會失敗,這是因爲 32 位整數無法存放 64 位的指針。例如,不要做下面的事情:

 

unsigned int i, *ptr;
i = (unsigned) ptr;

不要使用指針來存放 int 類型的值。例如,不要做下面的事情;

 

int *ptr;
int i;
ptr = (int *) i;

    如果在表達式中混合使用無符號和有符號的 32 位整數,並將其賦值給一個有符號的 long 類型,那麼將其中一個操作數轉換成 64 位的類型。這會導致其他操作數也被轉換成 64 位的類型,這樣在對錶達式進行賦值時就不需要再進行轉換了。另外一種解決方案是對整個表達式進行轉換,這樣就可以在賦值時進行符號擴展。例如,考慮下面這種用法可能會出現的問題:

long n;
int i = -2;
unsigned k = 1;
n = i + k;

從數學計算上來說,上面這個黑體顯示的表達式的結果應該是 -1 。但是由於表達式是無符號的,因此不會進行符號擴展。解決方案是將一個操作數轉換成 64 位類型(下面的第一行就是這樣),或者對整個表達式進行轉換(下面第二行):

 

n = (long) i + k;
n = (int) (i + k);





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章