说明
- 在工作(芯片设计公司开发)中,使用到的平台较多,交叉编译工具链有32位的也有64位的。
- 所谓32位/64位是硬件层次的概念,是指处理单元的三大总线(数据总线,控制总线,地址总线)是32根导线还是64根导线,每一根导线可以传输1位数据,所以是32位和64位,64位的cpu可以在软件层次兼容32位程序,安装32位系统也能跑32位程序,但是32位的cpu当前无法兼容64位的程序,理论上软件也能实现兼容,但是好像没有必要性。
- 编程方面,希望程序能够兼容32位和64位,这样换平台只需要重新编译就好了,不需要进行修改,节省工作量,也方便代码维护。
数据模型
- 硬件层次,32位cpu可以一次读取32位数据,64位cpu可以一次读取64位数据,但是软件层次,会有很多种需求,例如:一次读取1个字节数据,一次读取2个字节的数据等,为了满足多样的需求,编程语言需要提供给用户一个数据模型,即:a 对应 1个字节的数据,b 对应的 2的字节的数据,还是 a 对应 2 个字节的数据,b对应3个字节的数据。
- 理所应当,该数据模型需要足够通用,并且能满足用户的所有需求,由于硬件的性质,二进制就非常合适,所以大部分数据类型都是呈2的倍数关系。
- 数据模型是软件概念,硬件每一次读取依然是32位/64位,软件再取其中的相应位数。
32位数据模型 (ILP32)和64位数据模型(LP64)
- 32位数据模型和64位数据模型是不同的。
- C语言在32位cpu下使用32位数据模型ILP32,"ILP32"的意思是32位数据模型int、long、指针(p)都是32位的。LP64类似,表示C语言在64位cpu环境下的数据模型是LP64,此时的long和指针已为64位。
- 基础数据类型
数据类型 | 数据长度(32位环境) | 数据长度(64位环境) | 是否signed |
---|
char | 8 | 8 | Y | short | 16 | 16 | Y | int | 32 | 32 | Y | long | 32 | 64 | Y | long long | 64 | 64 | Y | point | 32 | 64 | N | size_t | 32 | 64 | N | ssize_t | 32 | 64 | Y | off_t | 32 | 64 | Y |
- 数据模型是为了满足软件层次的需求设计的,因此不管是多少位环境,char永远是8位,short永远是16位,int永远是32位,不会缺失。
- 数据模型中几乎所有数据类型都小于硬件位数,例如:32位模型,都小于32或等于32位,64位模型中的数据类型都小于64位,是因为希望所有数据类型都小于一次硬件读取单元,32位模型的long long是一个特例,是后面由于软件新需求(64单元)而添加的。
- 基础的数据类型(char,short,int,long)是倍数关系,直至顶,而size_t,off_t 是软件需求触发的数据类型(size_t :个数,off_t : 偏移)为了足够满足软件需求,数据类型长度按最大值分配(硬件读取单元长度)。
数据模型设置
- 由于数据模型是软件概念,因此在编译时可以手动设置应用的模型,默认匹配系统。
- gcc 设置如下:
m32指定编译为32位应用程序;
make CFLAGS=-m32
m64指定编译为64位应用程序;
make CFLAGS=-m64
变长数据模型导致的问题
- 不同数据模型下数据类型的长度不固定可能会导致很多奇怪的问题,编程时需要特别注意,例如:
- 打印问题(size_t)
- size_t的数据长度 32/64位环境上是不同的,打印时无法使用‘ld’或’d’等。
size_t val = xxx;
printf("%zu",val);
- 数据截断问题
- 64位环境下,size_t赋值给int类型就可能发生数据截断。
- 结构体内存对齐问题
- 结构体中变量长度在不同环境下发生变化,内存对齐可能会发生改变。
定长数据类型
- 32/64位环境下,不同数据类型的长度可能不同,但是有时候会存在这样的需求,在不同环境变量的数据长度需要一致,特别是网络传输中,这就是定长数据类型,例如:uint64_t, uint32_t 等。
- 定长数据出现的问题
- uint64_t 打印问题
32位环境下打印64位数据使用%llu,64位环境下使用%lu就行,为了兼容采用 PRIu64 宏,如下:
#include <inttypes.h>
uint64_t num=10;
printf("uint64: %" PRIu64 "\n", num);
|