Prayer

在一般中寻求卓越
posts - 1256, comments - 190, trackbacks - 0, articles - 0
  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

64bit需要做的改变

Posted on 2009-10-09 09:29 Prayer 阅读(930) 评论(0)  编辑 收藏 引用 所属分类: C/C++LINUX/UNIX/AIX
最近64bit以更低的代价开放到了广大的用户面前,对于需要更大的内存空间,或者更精确的浮点数计算的开发者来说,无疑提供了更多的方便。这篇文章的目的是要读者了解对于32bit的源代码向64bit转移所要做的工作。

事实上来说,32bit的代码可以很方便的移植到64位机上,因为64位提供了对32位很好的支持,只有在你有以下的需要的时候,尝试去修改你的代码

  • 获得超过4GB内存的支持。
  • 获得超过2G大小文件的支持
  • 获得更精确的浮点数运算
  • 获得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


  • 只有注册用户登录后才能发表评论。
    网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理