64bit需要做的改變
Posted on 2009-10-09 09:29 Prayer 閱讀(949) 評(píng)論(0) 編輯 收藏 引用 所屬分類: C/C++ 、LINUX/UNIX/AIX
事實(shí)上來(lái)說(shuō),32bit的代碼可以很方便的移植到64位機(jī)上,因?yàn)?4位提供了對(duì)32位很好的支持,只有在你有以下的需要的時(shí)候,嘗試去修改你的代碼
否則,只需要重新編譯你的32位代碼,that's ok!
1。ILP32和LP64 數(shù)據(jù)模式
大多數(shù)unix系統(tǒng)的數(shù)據(jù)模式是采用LP64bit的,long和pointer是8個(gè)字節(jié)64位的,相對(duì)于32bit的4個(gè)字節(jié)。將來(lái)windows或許會(huì)采用一種數(shù)據(jù)模式LLP64,只是對(duì)于pointer采用64bit,其他的和32位一樣。這里以表格的方式顯示兩者之間的差別
Data Type | ILP32 (bits) | LP64 (bits) |
char | 8 | No change |
short | 16 | No change |
int | 32 | No change |
long long | 64 | No change |
long | 32 | 64 |
pointer | 32 | 64 |
基本上移植到64bit的錯(cuò)誤都是來(lái)自誤以為int long pointer都是64bit的,實(shí)際則不然。下面是一個(gè)程序演示了這個(gè)錯(cuò)誤。
1 int *myfunc(int i)
2 {
3 return(&i);
4 }
5
6 int main(void)
7 {
8 int myint;
9 long mylong;
10 int *myptr;
11
12 char *name = (char * ) getlogin();
13
14 printf("Enter a number %s: ", name);
15 (void) scanf("%d", &mylong);
16 myint = mylong;
17 myptr = myfunc(mylong);
18 printf("mylong: %d pointer: %x \n", mylong, myptr);
19 myint = (int)mylong;
20 exit(0);
21
22 }
上面的代碼是錯(cuò)誤的,需要進(jìn)行修正。
移植的第一項(xiàng)工作,是讓編譯器能檢測(cè)到64位的錯(cuò)誤。這根據(jù)編譯器的不同而變化。對(duì)于IBM的XL 編譯器家族來(lái)說(shuō),有用的參數(shù)是-qwarn64 -qinfo=pro. 把你的代碼編譯成64bit需要加上-q64,對(duì)于gcc編譯器來(lái)說(shuō)則是加上-m64,下面列舉一些編譯64位一些有用的gcc參數(shù)。
Option | Description |
-Wpadded | Warns that padding was added to the structure. |
-Wformat | Check calls to printf and scanf have correct format strings. |
-Wsign-compare | Warn when a comparison between signed and unsigned values could produce an incorrect result when the signed value is converted to unsigned. |
-Wsign-compare | Warn when a comparison between signed and unsigned values could produce an incorrect result when the signed value is converted to unsigned. |
-Wconversion | Warn if a prototype causes a type conversion that is different from what would happen to the same argument in the absence of a prototype. |
-Wpointer-arith | Warn about anything that depends on the function pointer or of void *. |
下面演示以下編譯上面代碼所出現(xiàn)的提示信息。
% xlc -q64 -qformat=all -qwarn64 test.c
"test.c", line 12.30: 1506-745 (I) 64-bit portability: possible incorrect pointer through conversion of int type into pointer.
"test.c", line 15.36: 1506-1191 (W) Invalid int format for long argument type in argument 2.
"test.c", line 16.25: 1506-742 (I) 64-bit portability: possible loss of digits through conversion of long int type into int type.
"test.c", line 17.32: 1506-742 (I) 64-bit portability: possible loss of digits through conversion of long int type into int type.
"test.c", line 18.39: 1506-1191 (W) Invalid int format for long argument type in argument 2.
"test.c", line 19.25: 1506-742 (I) 64-bit portability: possible loss of digits through conversion of long int type into int type.
2。缺少原型的轉(zhuǎn)換方面的考慮。
上面的代碼char *name = (char * ) getlogin();
會(huì)出現(xiàn)錯(cuò)誤的,32位的int要轉(zhuǎn)換成64位會(huì)出現(xiàn)錯(cuò)誤的,要避免這種錯(cuò)誤,include正確的頭文件<unistd.h>,里面有g(shù)etlogin()的原型。
3。制定數(shù)據(jù)格式的錯(cuò)誤
(void) scanf("%d", &mylong);
要避免這個(gè)錯(cuò)誤,修改成這樣(void) scanf("%ld", &mylong);
printf("mylong: %d pointer: %x \n", mylong, myptr);
修改成printf("mylong: %ld pointer: %p \n", mylong, myptr);
4.賦值的錯(cuò)誤
myint = mylong;
錯(cuò)誤出現(xiàn)在64位的值賦給32位的
5.數(shù)值參數(shù)的傳輸錯(cuò)誤
myptr = myfunc(mylong);會(huì)出現(xiàn)傳遞時(shí)的轉(zhuǎn)換類型錯(cuò)誤
6.類型強(qiáng)制轉(zhuǎn)換出現(xiàn)的錯(cuò)誤
myint = (int)mylong;
int length = (int) strlen(str);
在LP64模式中strlen返回的是unsigned long,雖然一般不會(huì)出現(xiàn)錯(cuò)誤,但是在大于2GB的時(shí)候,盡管不大可能,但是會(huì)出現(xiàn)潛在的錯(cuò)誤。
7.更多的微妙的錯(cuò)誤
編譯器會(huì)發(fā)現(xiàn)大多數(shù)的潛在的類型轉(zhuǎn)換方面的錯(cuò)誤,但是你不能僅僅依賴于編譯器。
#define INVALID_POINTER_VALUE 0xFFFFFFFF
在32位上上面的宏定義會(huì)被經(jīng)常地用來(lái)測(cè)試-1,但是在64位機(jī)上,這個(gè)數(shù)值卻不是-1,而是4294967295。在64位機(jī)上-1是0xFFFFFFFFFFFFFFFF。要避免這個(gè)錯(cuò)誤,利用const來(lái)聲明,并指定signed還是unsigned
const signed int INVALID_POINTER_VALUE = 0xFFFFFFFF;
上面的代碼在32位和64位機(jī)上都會(huì)工作的很好。
還有就是32位機(jī)上的代碼如下
int **p; p = (int**)malloc(4 * NO_ELEMENTS);
這個(gè)代碼犯了如下的錯(cuò)誤就是認(rèn)為sizeof(int) == sizeof(int *)
實(shí)際上在64位上是不對(duì)的。
8.signed和unsigned的錯(cuò)誤
long k;
int i = -2;
unsigned int j = 1;
k = i + j;
printf("Answer: %ld\n", k);
以上的代碼在32位機(jī)上會(huì)得出-1。但是在64位機(jī)上卻不是的。結(jié)果會(huì)是4294967295。原因是i+j得到的會(huì)是unsigned的值,而這個(gè)值卻會(huì)賦給long的k,要避免這個(gè)錯(cuò)誤,需要進(jìn)行修改如下:
k = i + (int)j;
9.union的錯(cuò)誤
下面是一個(gè)很常見(jiàn)的結(jié)構(gòu)體:
typedef struct {
unsigned short bom;
unsigned short cnt;
union {
unsigned long bytes;
unsigned short len[2];
} size;
} _ucheader_t;
32位機(jī)上會(huì)工作的很好,但是在64位機(jī)上不然。原因是64位機(jī)上long 等于4個(gè)short.要把unsigned long 修改成 unsigned int. 才能工作好。必須要注意這個(gè)細(xì)節(jié)。
10.big endian和little endian導(dǎo)致的錯(cuò)誤
在32位機(jī)上的代碼有些移植到64位機(jī)上出現(xiàn)錯(cuò)誤與否還和機(jī)器的big endian或者little endian有關(guān)。比如下面的代碼。
long k;
int *ptr;
int main(void)
{
k = 2 ;
ptr = &k;
printf("k has the value %ld, value pointed to by ptr is %ld\n", k, *ptr);
return 0;
}
這段代碼在32bit機(jī)上編譯不會(huì)出現(xiàn)錯(cuò)誤,因?yàn)閘ong 和pointer都是32bit,但是在64位機(jī)上不然,盡管如此,如果在little endian的64位機(jī)上編譯的話,仍然會(huì)得到正確的2,但是在big endian的情況下,則會(huì)得到0。
如果有疑問(wèn)的話,下面詳細(xì)解釋一下。
Memory Address | Little Endian LP64 | Big Endian LP64 |
0x0x7fbfffdca0 ptr | 0x02 | 0x00 |
0x0x7fbfffdca1 | 0x00 | 0x00 |
0x0x7fbfffdca2 | 0x00 | 0x00 |
0x0x7fbfffdca3 | 0x00 | 0x00 |
0x0x7fbfffdca4 | 0x00 | 0x00 |
0x0x7fbfffdca5 | 0x00 | 0x00 |
0x0x7fbfffdca6 | 0x00 | 0x00 |
0x0x7fbfffdca7 | 0x00 |
0x02 |
11.64位機(jī)上性能的降低
這個(gè)問(wèn)題是由于64位機(jī)的數(shù)據(jù)結(jié)構(gòu)的膨脹,對(duì)于內(nèi)存需要的增加,以及存儲(chǔ)空間的增加所致。要減少這方面的損失,你可以精心改變你的數(shù)據(jù)結(jié)構(gòu)中變量定義的順序。比如下面的例子
12.如何測(cè)試你的64位代碼
定義一些宏來(lái)完成目標(biāo),下面是示例:
#if defined (__LP64__) || defined (__64BIT__) || defined (_LP64) || (__WORDSIZE == 64)
printf("I am LP64\n");
#else
printf("I am ILP32 \n");
#endif
13.64位文件和32位文件之間的彼此讀取問(wèn)題
#include <stdio.h>
#include <inttypes.h>
struct on_disk
{
/* ILP32|LP64 Sharing Issue: This should change to int32_t */
long foo;
};
int main()
{
FILE *file;
struct on_disk data;
#ifdef WRITE
file=fopen("test","w");
data.foo = 65535;
fwrite(&data, sizeof(struct on_disk), 1, file);
#else
file = fopen("test","r");
fread(&data, sizeof(struct on_disk), 1, file);
printf("data: %ld\n", data.foo);
#endif
fclose(file);
}
代碼不能很好地在32位和64為對(duì)同一個(gè)文件的操作過(guò)程中工作。要fix這個(gè)錯(cuò)誤,可以設(shè)置宏定義,以在64位機(jī)上對(duì)foo聲明為int32_t。要注意這個(gè)細(xì)節(jié)。
14.fortran和c語(yǔ)言混合的問(wèn)題
下面兩個(gè)程序演示了這個(gè)錯(cuò)誤
void FOO(long *l);
main ()
{
long l = 5000;
FOO(&l);
}
subroutine foo( i )
integer i
write(*,*) 'In Fortran'
write(*,*) i
return
end subroutine foo
要避免這個(gè)錯(cuò)誤,需要在fortran語(yǔ)言中把i聲明為INTEGER*8,這相當(dāng)于64位的long.
最后,結(jié)論:64位機(jī)提供了對(duì)于更多的計(jì)算方面的更好的支持,盡管32位代碼一般來(lái)說(shuō)可以移植的很好,但是,得注意這些細(xì)節(jié)方面的問(wèn)題,這樣才能使你的代碼一直工作更順利。