1.背景介绍
在一些程序中经常看到uint8_t uint32_t等数据类型,那么它与我们常见的int类型有什么区别呢。
2.协议介绍
stdint.h头文件是为了代码的可移植性而推出的,C99中就已经规范了。
3.代码可移植性
3.1 数据类型的差异
大部分的32位系统采用的是ILP32(这个对于嵌入式是在ABI文档中规定的ARM的ABI文档),大部分64位系统采用的是LP64。各个系统基本数据类型长度对比如图所示:
TYPE | ILP32 | LP64 | LLP64 | LP332 | ILP64 |
---|---|---|---|---|---|
char | 1 | 1 | 1 | 1 | 1 |
short | 2 | 2 | 2 | 2 | 2 |
int | 4 | 4 | 4 | 2 | 8 |
long | 4 | 8 | 4 | 4 | 8 |
long long | 8 | 8 | 8 | 8 | 8 |
float | 4 | 4 | 4 | 4 | 4 |
double | 8 | 8 | 8 | 8 | 8 |
size_t | 4 | 8 | 8 | 4 | 8 |
pointer | 4 | 8 | 8 | 4 | 8 |
3.2 ILP32 与LP64的对比
Type | ILP32 sizeof | ILP32 print | LP64 sizeof | LP64 print | 备注 |
---|---|---|---|---|---|
bool | 1 | %u | 1 | %u | C++ |
char | 1 | %d或%c | 1 | %d或%c | |
unsigned char | 1 | %u | 1 | %u | |
short | 2 | %d | 2 | %d | |
unsigned short | 2 | %u | 2 | %u | |
int | 4 | %d | 4 | %d | |
unsigned int | 4 | %u | 4 | %u | |
long | 4 | %ld | 8 | %ld | 有差异 |
unsigned long | 4 | %lu | 8 | %lu | 有差异 |
long int | 4 | %ld | 8 | %ld | 有差异 |
unsigned long int | 4 | %lu | 8 | %lu | 有差异 |
long long | 8 | %lld | 8 | %lld | |
unsigned long long | 8 | %llu | 8 | %llu | |
type * | 4 | %p | 8 | %p | 有差异 |
pid_t | 4 | %d | 4 | %d | |
socklen_t | 4 | %u | 4 | %u | |
off_t | 4 | %zd | 8 | %zd | 有差异 |
time_t | 4 | %zd | 8 | %zd | 有差异 |
pthread_t | 4 | %zu | 8 | %zu | 有差异 |
size_t | 4 | %zu | 8 | %zu | 有差异 |
ssize_t | 4 | %zd | 8 | %zd | 有差异 |
基于可移植性要求,在32位和64位条件下,可变长度的数据类型可能导致兼容性错误
3.3 数据类型统一化
类型定义 | ILP32 | LP64 | 使用场景及代替类型 | |
---|---|---|---|---|
void | - | - | - | void,无类型,仅用于占位和通用指针定义 |
char | 1 | 1 | %c | 对于字符串、数组直接使用原生char |
int8_t | 1 | 1 | %d | 对于1字节整型使用int8_t,uint8_t |
uint8_t | 1 | 1 | %u | 对于1字节整型使用int8_t,uint8_t |
int16_t | 2 | 2 | %d | 代替short |
uint16_t | 2 | 2 | %u | 代替unsigned short |
int32_t | 4 | 4 | %d | 代替int |
uint32_t | 4 | 4 | %u | 代替unsigned int |
int64_t | 8 | 8 | %PRId64 | 代替long long、宏实现代码兼容 |
uint64_t | 8 | 8 | %PRIu64 | 代替unsigned long long、宏实现代码兼容 |
float | 4 | 4 | %f | 单精度浮点数 |
double | 8 | 8 | %lf | 双精度浮点数 |
bool | 1 | 1 | %d | 布尔类型 |
uintptr_t | 4 | 8 | %zu | 会根据32位和64位的不同定义为不同的长度,用于可能存储指针的场景 |
type * | 4 | 8 | %p | type *,可变长度类型,与uintptr_t等价,存在类型转换时建议使用uintptr_t |
nullptr_t | 4 | 8 | %p | 指针初始化 |
pid_t | 4 | 4 | %d | Linux内置,固定长度 |
socklen_t | 4 | 4 | %u | Linux内置,固定长度 |
off_t/time_t | 4 | 8 | %zd | 可变长度类型,有符号 |
size_t/pthread_t | 4 | 8 | %zu | 可变长度类型,无符号,仅用于调用库函数的兼容性要求(比如底层API中使用了size_t) |
4.代码实践
4.1 代码示例
在线编译环境:https://godbolt.org/ X86-64 gcc 9.4
#include <stdio.h>
#include <stdint.h>
int32_t main()
{
printf("int: %d\n",sizeof(int));
printf("long:%d\n",sizeof(long));
printf("uint32_t:%d\n",sizeof(uint32_t));
printf("uint64_t:%d\n",sizeof(uint64_t));
}
int: 4
long:8
uint32_t:4
uint64_t:8
4.1 stdint.h
以下为从gcc编译链提取的stdint头文件,x86_64 x86_64 GNU/Linux
#ifndef __int8_t_defined
# define __int8_t_defined
typedef signed char int8_t;
typedef short int int16_t;
typedef int int32_t;
# if __WORDSIZE == 64
typedef long int int64_t;
# else
__extension__
typedef long long int int64_t;
# endif
#endif
/* Unsigned. */
typedef unsigned char uint8_t;
typedef unsigned short int uint16_t;
#ifndef __uint32_t_defined
typedef unsigned int uint32_t;
# define __uint32_t_defined
#endif
#if __WORDSIZE == 64
typedef unsigned long int uint64_t;
#else
__extension__
typedef unsigned long long int uint64_t;
#endif
可以看见uint8_t也是对int等不同的类型的重定义而已,只是实现了跨平台;
5.参考文献
1.ISO/IEC 9899 :TC3
2.鸿蒙代码移植规范