03-可移植的数据类型
23.1.1 可移植的数据类型
C语言中的标准数据类型int、long的长度直接与平台相关,在驱动中,关键部分代码直接使用这些类型时需要特别小心。表23.1给出了在几个不同的平台下char、short、int、long、ptr、long long的长度。
表23.1 不同平台下char、short、int、long、ptr、long long的长度
| arch | char | short | int | long | ptr | long long | | :----- | :----- | :----- | :----- | :----- | :----- | :----- | :----- | :----- | | i386 | 1 | 2 | 4 | 4 | 4 | 8 | | Alpha | 1 | 2 | 4 | 8 | 8 | 8 | | armv4l | 1 | 2 | 4 | 4 | 4 | 8 | | ia64 | 1 | 2 | 4 | 8 | 8 | 8 | | M68K | 1 | 2 | 4 | 4 | 4 | 8 | | MIPS | 1 | 2 | 4 | 4 | 4 | 8 | | PaverPC | 1 | 2 | 4 | 4 | 4 | 8 | | Sparc | 1 | 2 | 4 | 4 | 4 | 8 | | Sparc64 | 1 | 2 | 4 | 4 | 4 | 8 | | x86_64 | 1 | 2 | 4 | 8 | 8 | 8 |
因此,在Linux系统中,针对不同的体系结构重新typedef出了u8、u16、u32、u64、s8、s16、s32、s64等类型。例如,在i386/arm下这些类型的定义如代码清单23.1所示,而在ppc64下这些类型的定义则如代码清单23.2所示,可见影响C语言基本数据类型大小的主要因素是CPU字长。
代码清单23.1 i386/arm平台下u8、u16、u32、u64、s8、s16、s32、s64的定义
1 typedef signed char s8;
2 typedef unsigned char u8;
3
4 typedef signed short s16;
5 typedef unsigned short u16;
6
7 typedef signed int s32;
8 typedef unsigned int u32;
9
10 typedef signed long long s64;
11 typedef unsigned long long u64;
代码清单23.2 ppc64平台下u8、u16、u32、u64、s8、s16、s32、s64的定义
1 typedef signed char s8;
2 typedef unsigned char u8;
3
4 typedef signed short s16;
5 typedef unsigned short u16;
6
7 typedef signed int s32;
8 typedef unsigned int u32;
9
10 typedef signed long s64;
11 typedef unsigned long u64;
由于u8、u16、u32、u64、s8、s16、s32、s64是被针对不同的体系结构单独定义的,因此,在任何平台下对其进行sizeof运算的结果都是不变的,是确定长度的数据类型。但是,这些类型都应该只在内核空间使用。在Linux用户空间中,如果要使用确定长度的数据类型,应该使用上述定义加“ ”的版本,如_ u8、 u16、 u32等。鉴于此,设备驱动中如果要向用户空间导出头文件,在头文件中也应该定义 sxx、 _uxx等数据类型。
上面给出的uxx、sxx、_ uxx、 _sxx类型定义都是Linux系统所特有的,为了更好地向其他平台移植,驱动中最好使用int8_t、int16_t、int32_t、uint8_t、uint16_t、uint32_t、int64_t、uint64_t这些C99标准确定长度类型。
Linux系统中定义了许多以_t为后缀的数据类型,这些类型用在内核的一些常用功能的实现中,如dma_addr_t、uid_t、gid_t、size_t、ssize_t、pid_t、loff_t等,这些类型的使用将使内核屏蔽实际数据类型间存在的差异。例如,file_operations中的read()、write()成员函数返回值为ssize_t类型,llseek()返回的是loff_t类型。此外,ssize_t、pid_t这些类型也被赋予了一些含义,这一点从名字就可以看出来,如pid_t是进程ID类型。