Linux 是同时支持32-bits和64-bit跨平台的操作系统。随着双核机器越来越多,因此有很多应用需要适应不同的操作系统,也就是说需要同时兼容32位和64位。

首先我们来看32-bits和64-bits对C语言标准支持的不同。从表一可以看,通常LINUX32-bit 使用ILP32,而64-bits使用LP64标准,在32-bits标准中,int, long,pointer都是分配32it,

而64-bits系统中则不一样,pointer和long 是64位,但是int是32位。

移植linux 应用到64-bit系统_移植

由于这两种系统的主要不同点是在long和point上,而这两种数据类型又是开发时经常要进行声明赋值,特别是一些对一些常见结构体数据大小的计算上,差异还是非常明显的。如下面的数据结构:

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

移植linux 应用到64-bit系统_移植_02

注意这时候的sizeof(test)  在32bit和64bit上是分了不同大小空间的。32bit 上是4+4+4+4+4=20而64bit是8(空半)+8+8(空半)+8=32

Linux 应用兼容64bit/32bit的一些基本原则

1. 声明

如果这个变量在32bit上要求32bit,但是在64bit上要求64bit,应该定义成long型或者如果这个变量指的是一个地址,这里就需要用size_t。

#if __WORDSIZE == 64 #define BASE_ADDRESS 0x00002AB0E7E16000 #else #define BASE_ADDRESS 0x88000000 /* Approx 2^32 - 2GBytes */ #endif Unsigned int base_address; // size_t base_address【如果32位,没有问题,在64位就会有问题,这里需要用size_t】

如果有以下这样的转换也不行的,g_shm_mem_ptr 是指针,在32bit可以用int,但到64位需要用long,

if (g_shm_mem_ptr != 0 && (int) g_shm_mem_ptr != -1)改成 if (g_shm_mem_ptr != 0 && (long) g_shm_mem_ptr != -1)

如果这个变量要求分配的空间在不同系统上是一样的,因些对声明long型改成声明int型进行兼容。

typedef union C_data_value { int int_data; char *string_data; long long_data;//int long_data【32bit long data分配32bit,但是64bit上则分配64bit,为了兼容,需要修改这里数据类型为int】 float float_data; unsigned int unsigned_int_data; unsigned long unsigned_long_data;// unsigned int unsigned_int_data; } C_DATA_VALUE_T;

其它像unsigned long int实际在32位系统就是unsigned long. 取值范围是0~4294967295(232 -1)

/* UINT4 defines a four byte word */ typedef unsigned long //int UINT4;

2. 赋值

不要使用不同类型的变量直接赋值。如

o int 与long 转换

 

32-bits

64-bits

int i;

32bit

32bit

long l ;

32bit

64bit

i =l  ;

OK

Error

o int 与pointer

 

32-bits

64-bits

unsigned int i,* ptr;

32bit

32bit

i=(unsigned)ptr;

OK

Error

   

*ptr 可以是32bit,但ptr 一定是64bit

o       pointer 与(int *)

 

32-bits

64-bits

int * ptr;

32bit

32bit

int i;

32bit

32bit

ptr =(int *)i;

OK

Error

3. 字符标志位用来表示特定含义

如用0xFFFFFFF 每一个字符位来表示特定含义的用法,需要注意在64位它就变成0x00000000FFFFFFFF,计算时特别要小心。如果要声明0xFFFFFFFFFFFFFF可用long x=-L来声明。

4. 字节序大小头不同

我们知道,字节序分成big-endian 和little-endian. Little-endian 意味着较低内存地址用来存储不重要数据,(所谓不重要数据通常指的是数据的未位,如0x12345678中的5678。big-endian 则相反。如果以内存的排列顺序(从低到高由左向右来看),就会出现高位数据放在前面变成56781234。采用little-endian通常都是intel体系架构主机。

另外,32-bits是两个字节两个字节(16bit)的分配,而64-bits是四个字节(32bit)一次分配。如0x12345678在32bit机器上

0x12345678 big-endian

 

32-bits

64-bits

3

0x78

 

2

0x56

 

1

0x34

0x5678

0

0x12       

0x1234

0x12345678 little-endian

 

32-bits

64-bits

3

0x12

 

2

0x34

 

1

0x56

0x1234

0

0x78

0x5678

在两台不同主机进行数据传输的网络字节序目前采用的是big-endian.

从开发的角度来说,由于glibc库已经实现了内部的封装,大家可以查看以下库文件

/usr/include/bits/endian.h

/usr/include/endian.h

/usr/include/bits/byteswap.h

/usr/include/byteswap.h

我们只要使用与机器无关的函数如ntohl等就可以了。

通常意义上我们都使用函数htonl和ntohl及htons,ntohs进行转换。注意这些函数都不能对64位数据进行转换,

#if __WORDSIZE == 64

#if __BYTE_ORDER == __BIG_ENDIAN

/* The host byte order is the same as network byte order,

so these functions are all just identity. */

#define ntohlong(x) (x)

#define htonlong(x) (x)

#else

#if __BYTE_ORDER == __LITTLE_ENDIAN

#define ntohlong(x) __bswap_64 (x)

#define htonlong(x) __bswap_64 (x)

#endif

#endif

#else

#define ntohlong(x) ntohl (x)

#define htonlong(x) htonl (x)

#endif

#endif

5. 类型重定义

通常一些定义类型有一些跨平台作用,如size_t或者一些明确大小的如int32_t,uint32_t。通常如

int buffersize = (int)sizeof(something);最好的方法是

size_t buffersize = (size_t)sizeof(something)

6. 移位算法

对一些未显示指明类型的常量通常会隐含风险,如12这实际上是int类型。如果long i =12 这实际上是long i =(int)12在32位系统,但在64位系统上long i = (int)12,就会带来问题。

需要声明long i = 1L;;

7. 格式化输出printf

printf()进行格式输出时,不同参数的数据类型在32bit和64 bit下输出结果是完全不一样的。这样的隐藏错误有时很难发现,所以一定要注意检查是否需要修改相应的格式字符串,否则编译器连警告信息都不会出现。

比喻在32bits平台,%d可以打印long也可以是int,但在64位不行,需要使用%ld或者%u

printf("Indx %dld",strtol(tableInfo->tbl_index,NULL,0));

%d和%u的区别

unsigned int dwValue;

printf(“%d”, dwValue);

在dwValue的值大于0x7FFFFFFF时,输出的结果会变成负数。

正确的程序应该为:

printf(“%u”, dwValue);

%x  在32bit用来输出地址,但是在64 上不用,必须用%p

%d在32bit用来输出int and long,在64上不行,必须用%ld