最近64bit以更低的代价开放到了广大的用户面前,对于需要更大的内存空间,或者更精确的浮点数计算的开发者来说,无疑提供了更多的方便。这篇文章的目的是要读者了解对于32bit的源代码向64bit转移所要做的工作。
获得64位机优化的数学运算库
否则,只需要重新编译你的32位代码,that's ok!
1。ILP32和LP64 数据模式
大多数unix系统的数据模式是采用LP64bit的,long和pointer是8个字节64位的,相对于32bit的4个字节。将来windows或许会采用一种数据模式LLP64,只是对于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的错误都是来自误以为int long pointer都是64bit的,实际则不然。下面是一个程序演示了这个错误。
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      }
上面的代码是错误的,需要进行修正。
移植的第一项工作,是让编译器能检测到64位的错误。这根据编译器的不同而变化。对于IBM的XL 编译器家族来说,有用的参数是-qwarn64 -qinfo=pro. 把你的代码编译成64bit需要加上-q64,对于gcc编译器来说则是加上-m64,下面列举一些编译64位一些有用的gcc参数。
    
        
            | 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 *. | 
    
下面演示以下编译上面代码所出现的提示信息。
%  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。缺少原型的转换方面的考虑。
上面的代码char *name = (char * ) getlogin();
会出现错误的,32位的int要转换成64位会出现错误的,要避免这种错误,include正确的头文件<unistd.h>,里面有getlogin()的原型。
3。制定数据格式的错误
(void) scanf("%d", &mylong);
要避免这个错误,修改成这样(void) scanf("%ld", &mylong);
printf("mylong: %d  pointer: %x \n", mylong, myptr);
修改成printf("mylong: %ld  pointer: %p \n", mylong, myptr);
4.赋值的错误
myint = mylong;
错误出现在64位的值赋给32位的
5.数值参数的传输错误
myptr = myfunc(mylong);会出现传递时的转换类型错误
6.类型强制转换出现的错误
myint = (int)mylong;
int length = (int) strlen(str);
在LP64模式中strlen返回的是unsigned long,虽然一般不会出现错误,但是在大于2GB的时候,尽管不大可能,但是会出现潜在的错误。
7.更多的微妙的错误
编译器会发现大多数的潜在的类型转换方面的错误,但是你不能仅仅依赖于编译器。
#define INVALID_POINTER_VALUE 0xFFFFFFFF
在32位上上面的宏定义会被经常地用来测试-1,但是在64位机上,这个数值却不是-1,而是4294967295。在64位机上-1是0xFFFFFFFFFFFFFFFF。要避免这个错误,利用const来声明,并指定signed还是unsigned
const signed int INVALID_POINTER_VALUE = 0xFFFFFFFF;
上面的代码在32位和64位机上都会工作的很好。
还有就是32位机上的代码如下
int **p; p = (int**)malloc(4 * NO_ELEMENTS);
这个代码犯了如下的错误就是认为sizeof(int) ==  sizeof(int *)
实际上在64位上是不对的。
8.signed和unsigned的错误
long k;
int i = -2;
unsigned int j = 1;
k = i +  j;
printf("Answer: %ld\n", k);
以上的代码在32位机上会得出-1。但是在64位机上却不是的。结果会是4294967295。原因是i+j得到的会是unsigned的值,而这个值却会赋给long的k,要避免这个错误,需要进行修改如下:
k = i + (int)j;
9.union的错误
下面是一个很常见的结构体:
typedef struct {
    unsigned short bom;
    unsigned short cnt;
    union {
        unsigned long bytes;
        unsigned short len[2];
    } size;
} _ucheader_t;
32位机上会工作的很好,但是在64位机上不然。原因是64位机上long 等于4个short.要把unsigned long 修改成 unsigned int. 才能工作好。必须要注意这个细节。
10.big endian和little endian导致的错误
在32位机上的代码有些移植到64位机上出现错误与否还和机器的big endian或者little endian有关。比如下面的代码。
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机上编译不会出现错误,因为long 和pointer都是32bit,但是在64位机上不然,尽管如此,如果在little endian的64位机上编译的话,仍然会得到正确的2,但是在big endian的情况下,则会得到0。
如果有疑问的话,下面详细解释一下。
    
        
            | 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位机上性能的降低
这个问题是由于64位机的数据结构的膨胀,对于内存需要的增加,以及存储空间的增加所致。要减少这方面的损失,你可以精心改变你的数据结构中变量定义的顺序。比如下面的例子
 
 
 
12.如何测试你的64位代码
定义一些宏来完成目标,下面是示例:
#if defined (__LP64__) || defined (__64BIT__) || defined (_LP64) || (__WORDSIZE == 64)
   printf("I am LP64\n");
#else
   printf("I am ILP32 \n");
#endif
13.64位文件和32位文件之间的彼此读取问题
#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为对同一个文件的操作过程中工作。要fix这个错误,可以设置宏定义,以在64位机上对foo声明为int32_t。要注意这个细节。
14.fortran和c语言混合的问题
下面两个程序演示了这个错误
void FOO(long *l);
main ()
{
   long l = 5000;
   FOO(&l);
}
subroutine foo( i )
integer  i
write(*,*) 'In Fortran'
write(*,*) i
return
end subroutine foo
要避免这个错误,需要在fortran语言中把i声明为INTEGER*8,这相当于64位的long.
最后,结论:64位机提供了对于更多的计算方面的更好的支持,尽管32位代码一般来说可以移植的很好,但是,得注意这些细节方面的问题,这样才能使你的代码一直工作更顺利。
文章来源于http://www.lupaworld.com