1
0
wiki/dev/C/数据类型.md

737 lines
27 KiB
Markdown
Raw Normal View History

2022-03-30 15:37:37 +08:00
---
id: 数据类型
title: 数据类型
sidebar_position: 6
data: 2022年3月30日
---
C 语言的每一种数据都是有类型type编译器必须知道数据的类型才能操作数据。所谓“类型”就是相似的数据所拥有的共同特征那么一旦知道某个值的数据类型就能知道该值的特征和操作方式。
基本数据类型有三种字符char、整数int和浮点数float。复杂的类型都是基于它们构建的。
## 字符类型
字符类型指的是单个字符,类型声明使用`char`关键字。
```c
char c = 'B';
```
上面示例声明了变量`c`是字符类型,并将其赋值为字母`B`。
C 语言规定,字符常量必须放在单引号里面。
在计算机内部字符类型使用一个字节8位存储。C 语言将其当作整数处理,所以字符类型就是宽度为一个字节的整数。每个字符对应一个整数(由 ASCII 码确定),比如`B`对应整数`66`。
字符类型在不同计算机的默认范围是不一样的。一些系统默认为`-128`到`127`,另一些系统默认为`0`到`255`。这两种范围正好都能覆盖`0`到`127`的 ASCII 字符范围。
只要在字符类型的范围之内,整数与字符是可以互换的,都可以赋值给字符类型的变量。
```c
char c = 66;
// 等同于
char c = 'B';
```
上面示例中,变量`c`是字符类型赋给它的值是整数66。这跟赋值为字符`B`的效果是一样的。
两个字符类型的变量可以进行数学运算。
```c
char a = 'B'; // 等同于 char a = 66;
char b = 'C'; // 等同于 char b = 67;
printf("%d\n", a + b); // 输出 133
```
上面示例中,字符类型变量`a`和`b`相加,视同两个整数相加。占位符`%d`表示输出十进制整数因此输出结果为133。
单引号本身也是一个字符,如果要表示这个字符常量,必须使用反斜杠转义。
```c
char t = '\'';
```
上面示例中,变量`t`为单引号字符,由于字符常量必须放在单引号里面,所以内部的单引号要使用反斜杠转义。
这种转义的写法,主要用来表示 ASCII 码定义的一些无法打印的控制字符,它们也属于字符类型的值。
- `\a`:警报,这会使得终端发出警报声或出现闪烁,或者两者同时发生。
- `\b`:退格键,光标回退一个字符,但不删除字符。
- `\f`:换页符,光标移到下一页。在现代系统上,这已经反映不出来了,行为改成类似于`\v`。
- `\n`:换行符。
- `\r`:回车符,光标移到同一行的开头。
- `\t`制表符光标移到下一个水平制表位通常是下一个8的倍数。
- `\v`:垂直分隔符,光标移到下一个垂直制表位,通常是下一行的同一列。
- `\0`null 字符代表没有内容。注意这个值不等于数字0。
转义写法还能使用八进制和十六进制表示一个字符。
- `\nn`:字符的八进制写法,`nn`为八进制值。
- `\xnn`:字符的十六进制写法,`nn`为十六进制值。
```c
char x = 'B';
char x = 66;
char x = '\102'; // 八进制
char x = '\x42'; // 十六进制
```
上面示例的四种写法都是等价的。
## 整数类型
### 简介
整数类型用来表示较大的整数,类型声明使用`int`关键字。
```c
int a;
```
上面示例声明了一个整数变量`a`。
不同计算机的`int`类型的大小是不一样的。比较常见的是使用4个字节32位存储一个`int`类型的值但是2个字节16位或8个字节64位也有可能使用。它们可以表示的整数范围如下。
- 16位-32,768 到 32,767。
- 32位-2,147,483,648 到 2,147,483,647。
- 64位-9,223,372,036,854,775,808 到 9,223,372,036,854,775,807。
### signedunsigned
C 语言使用`signed`关键字,表示一个类型带有正负号,包含负值;使用`unsigned`关键字,表示该类型不带有正负号,只能表示零和正整数。
对于`int`类型,默认是带有正负号的,也就是说`int`等同于`signed int`。由于这是默认情况,关键字`signed`一般都省略不写,但是写了也不算错。
```c
signed int a;
// 等同于
int a;
```
`int`类型也可以不带正负号,只表示非负整数。这时就必须使用关键字`unsigned`声明变量。
```c
unsigned int a;
```
整数变量声明为`unsigned`的好处是同样长度的内存能够表示的最大整数值增大了一倍。比如16位的`signed int`最大值为32,767而`unsigned int`的最大值增大到了65,535。
`unsigned int`里面的`int`可以省略,所以上面的变量声明也可以写成下面这样。
```c
unsigned a;
```
字符类型`char`也可以设置`signed`和`unsigned`。
```c
signed char c; // 范围为 -128 到 127
unsigned char c; // 范围为 0 到 255
```
注意C 语言规定`char`类型默认是否带有正负号,由当前系统决定。这就是说,`char`不等同于`signed char`,它有可能是`signed char`,也有可能是`unsigned char`。这一点与`int`不同,`int`就是等同于`signed int`。
### 整数的子类型
如果`int`类型使用4个或8个字节表示一个整数对于小整数这样做很浪费空间。另一方面某些场合需要更大的整数8个字节还不够。为了解决这些问题C 语言在`int`类型之外,又提供了三个整数的子类型。这样有利于更精细地限定整数变量的范围,也有利于更好地表达代码的意图。
- `short int`(简写为`short`):占用空间不多于`int`一般占用2个字节整数范围为-3276832767)。
- `long int`(简写为`long`):占用空间不少于`int`至少为4个字节。
- `long long int`(简写为`long long`):占用空间多于`long`至少为8个字节。
```c
short int a;
long int b;
long long int c;
```
上面代码分别声明了三种整数子类型的变量。
默认情况下,`short`、`long`、`long long`都是带符号的signed即`signed`关键字省略了。它们也可以声明为不带符号unsigned使得能够表示的最大值扩大一倍。
```c
unsigned short int a;
unsigned long int b;
unsigned long long int c;
```
C 语言允许省略`int`,所以变量声明语句也可以写成下面这样。
```c
short a;
unsigned short a;
long b;
unsigned long b;
long long c;
unsigned long long c;
```
不同的计算机数据类型的字节长度是不一样的。确实需要32位整数时应使用`long`类型而不是`int`类型可以确保不少于4个字节确实需要64位的整数时应该使用`long long`类型可以确保不少于8个字节。另一方面为了节省空间只需要16位整数时应使用`short`类型需要8位整数时应该使用`char`类型。
### 整数类型的极限值
有时候需要查看当前系统不同整数类型的最大值和最小值C 语言的头文件`limits.h`提供了相应的常量,比如`SCHAR_MIN`代表 signed char 类型的最小值`-128``SCHAR_MAX`代表 signed char 类型的最大值`127`。
为了代码的可移植性,需要知道某种整数类型的极限值时,应该尽量使用这些常量。
- `SCHAR_MIN``SCHAR_MAX`signed char 的最小值和最大值。
- `SHRT_MIN``SHRT_MAX`short 的最小值和最大值。
- `INT_MIN``INT_MAX`int 的最小值和最大值。
- `LONG_MIN``LONG_MAX`long 的最小值和最大值。
- `LLONG_MIN``LLONG_MAX`long long 的最小值和最大值。
- `UCHAR_MAX`unsigned char 的最大值。
- `USHRT_MAX`unsigned short 的最大值。
- `UINT_MAX`unsigned int 的最大值。
- `ULONG_MAX`unsigned long 的最大值。
- `ULLONG_MAX`unsigned long long 的最大值。
### 整数的进制
C 语言的整数默认都是十进制数,如果要表示八进制数和十六进制数,必须使用专门的表示法。
八进制使用`0`作为前缀,比如`017`、`0377`。
```c
int a = 012; // 八进制相当于十进制的10
```
十六进制使用`0x`或`0X`作为前缀,比如`0xf`、`0X10`。
```c
int a = 0x1A2B; // 十六进制相当于十进制的6699
```
有些编译器使用`0b`前缀,表示二进制数,但不是标准。
```c
int x = 0b101010;
```
注意,不同的进制只是整数的书写方法,不会对整数的实际存储方式产生影响。所有整数都是二进制形式存储,跟书写方式无关。不同进制可以混合使用,比如`10 + 015 + 0x20`是一个合法的表达式。
`printf()`的进制相关占位符如下。
- `%d`:十进制整数。
- `%o`:八进制整数。
- `%x`:十六进制整数。
- `%#o`:显示前缀`0`的八进制整数。
- `%#x`:显示前缀`0x`的十六进制整数。
- `%#X`:显示前缀`0X`的十六进制整数。
```c
int x = 100;
printf("dec = %d\n", x); // 100
printf("octal = %o\n", x); // 144
printf("hex = %x\n", x); // 64
printf("octal = %#o\n", x); // 0144
printf("hex = %#x\n", x); // 0x64
printf("hex = %#X\n", x); // 0X64
```
## 浮点数类型
任何有小数点的数值,都会被编译器解释为浮点数。所谓“浮点数”就是使用 m * b<sup>e</sup> 的形式,存储一个数值,`m`是小数部分,`b`是基数(通常是`2``e`是指数部分。这种形式是精度和数值范围的一种结合,可以表示非常大或者非常小的数。
浮点数的类型声明使用`float`关键字,可以用来声明浮点数变量。
```c
float c = 10.5;
```
上面示例中,变量`c`的就是浮点数类型。
`float`类型占用4个字节32位其中8位存放指数的值和符号剩下24位存放小数的值和符号。`float`类型至少能够提供十进制的6位有效数字指数部分的范围为十进制的`-37`到`37`即数值范围为10<sup>-37</sup>到10<sup>37</sup>
有时候32位浮点数提供的精度或者数值范围还不够C 语言又提供了另外两种更大的浮点数类型。
- `double`占用8个字节64位至少提供13位有效数字。
- `long double`通常占用16个字节。
注意,由于存在精度限制,浮点数只是一个近似值,它的计算是不精确的,比如 C 语言里面`0.1 + 0.2`并不等于`0.3`,而是有一个很小的误差。
```c
if (0.1 + 0.2 == 0.3) // false
```
C 语言允许使用科学计数法表示浮点数,使用字母`e`来分隔小数部分和指数部分。
```c
double x = 123.456e+3; // 123.456 x 10^3
// 等同于
double x = 123.456e3;
```
上面示例中,`e`后面如果是加号`+`,加号可以省略。注意,科学计数法里面`e`的前后,不能存在空格。
另外,科学计数法的小数部分如果是`0.x`或`x.0`的形式,那么`0`可以省略。
```c
0.3E6
// 等同于
.3E6
3.0E6
// 等同于
3.E6
```
## 布尔类型
C 语言原来并没有为布尔值单独设置一个类型,而是使用整数`0`表示伪,所有非零值表示真。
```c
int x = 1;
if (x) {
printf("x is true!\n");
}
```
上面示例中,变量`x`等于`1`C 语言就认为这个值代表真,从而会执行判断体内部的代码。
C99 标准添加了类型`_Bool`,表示布尔值。但是,这个类型其实只是整数类型的别名,还是使用`0`表示伪,`1`表示真,下面是一个示例。
```c
_Bool isNormal;
isNormal = 1;
if (isNormal)
printf("Everything is OK.\n");
```
头文件`stdbool.h`定义了另一个类型别名`bool`,并且定义了`true`代表`1`、`false`代表`0`。只要加载这个头文件,就可以使用这几个关键字。
```c
#include <stdbool.h>
bool flag = false;
```
上面示例中,加载头文件`stdbool.h`以后,就可以使用`bool`定义布尔值类型,以及`false`和`true`表示真伪。
## 字面量的类型
字面量literal指的是代码里面直接出现的值。
```c
int x = 123;
```
上面代码中,`x`是变量,`123`就是字面量。
编译时,字面量也会写入内存,因此编译器必须为字面量指定数据类型,就像必须为变量指定数据类型一样。
一般情况下,十进制整数字面量(比如`123`)会被编译器指定为`int`类型。如果一个数值比较大,超出了`int`能够表示的范围,编译器会将其指定为`long int`。如果数值超过了`long int`,会被指定为`unsigned long`。如果还不够大,就指定为`long long`或`unsigned long long`。
小数(比如`3.14`)会被指定为`double`类型。
## 字面量后缀
有时候,程序员希望为字面量指定一个不同的类型。比如,编译器将一个整数字面量指定为`int`类型,但是程序员希望将其指定为`long`类型,这时可以为该字面量加上后缀`l`或`L`,编译器就知道要把这个字面量的类型指定为`long`。
```c
int x = 123L;
```
上面代码中,字面量`123`有后缀`L`,编译器就会将其指定为`long`类型。这里`123L`写成`123l`,效果也是一样的,但是建议优先使用`L`,因为小写的`l`容易跟数字`1`混淆。
八进制和十六进制的值,也可以使用后缀`l`和`L`指定为 Long 类型,比如`020L`和`0x20L`。
```c
int y = 0377L;
int z = 0x7fffL;
```
如果希望指定为无符号整数`unsigned int`,可以使用后缀`u`或`U`。
```c
int x = 123U;
```
`L`和`U`可以结合使用,表示`unsigned long`类型。`L`和`U`的大小写和组合顺序无所谓。
```c
int x = 123LU;
```
对于浮点数,编译器默认指定为 double 类型,如果希望指定为其他类型,需要在小数后面添加后缀`f`float或`l`long double
科学计数法也可以使用后缀。
```c
1.2345e+10F
1.2345e+10L
```
总结一下,常用的字面量后缀有下面这些。
- `f`和`F``float`类型。
- `l`和`L`:对于整数是`long int`类型,对于小数是`long double`类型。
- `ll`和`LL`Long Long 类型,比如`3LL`。
- `u`和`U`:表示`unsigned int`,比如`15U`、`0377U`。
`u`还可以与其他整数后缀结合,放在前面或后面都可以,比如`10UL`、`10ULL`和`10LLU`都是合法的。
下面是一些示例。
```c
int x = 1234;
long int x = 1234L;
long long int x = 1234LL
unsigned int x = 1234U;
unsigned long int x = 1234UL;
unsigned long long int x = 1234ULL;
float x = 3.14f;
double x = 3.14;
long double x = 3.14L;
```
## 溢出
每一种数据类型都有数值范围如果存放的数值超出了这个范围小于最小值或大于最大值需要更多的二进制位存储就会发生溢出。大于最大值叫做向上溢出overflow小于最小值叫做向下溢出underflow
一般来说,编译器不会对溢出报错,会正常执行代码,但是会忽略多出来的二进制位,只保留剩下的位,这样往往会得到意想不到的结果。所以,应该避免溢出。
```c
unsigned char x = 255;
x = x + 1;
printf("%d\n", x); // 0
```
上面示例中,变量`x`加`1`,得到的结果不是`256`,而是`0`。因为`x`是`unsign char`类型,最大值是`255`(二进制`11111111`),加`1`后就发生了溢出,`256`(二进制`100000000`)的最高位`1`被丢弃,剩下的值就是`0`。
再看下面的例子。
```c
unsigned int ui = UINT_MAX; // 4,294,967,295
ui++;
printf("ui = %u\n", ui); // 0
ui--;
printf("ui = %u\n", ui); // 4,294,967,295
```
上面示例中,常量`UINT_MAX`是 unsigned int 类型的最大值。如果加`1`,对于该类型就会溢出,从而得到`0`;而`0`是该类型的最小值,再减`1`,又会得到`UINT_MAX`。
溢出很容易被忽视,编译器又不会报错,所以必须非常小心。
```c
for (unsigned int i = n; i >= 0; --i) // 错误
```
上面代码表面看似乎没有问题,但是循环变量`i`的类型是 unsigned int这个类型的最小值是`0`不可能得到小于0的结果。当`i`等于0再减去`1`的时候,并不会返回`-1`,而是返回 unsigned int 的类型最大值,这个值总是大于等于`0`,导致无限循环。
为了避免溢出,最好方法就是将运算结果与类型的极限值进行比较。
```c
unsigned int ui;
unsigned int sum;
// 错误
if (sum + ui > UINT_MAX) too_big();
else sum = sum + ui;
// 正确
if (ui > UINT_MAX - sum) too_big();
else sum = sum + ui;
```
上面示例中,变量`sum`和`ui`都是 unsigned int 类型,它们相加的和还是 unsigned int 类型,这就有可能发生溢出。但是,不能通过相加的和是否超出了最大值`UINT_MAX`,来判断是否发生了溢出,因为`sum + ui`总是返回溢出后的结果,不可能大于`UINT_MAX`。正确的比较方法是,判断`UINT_MAX - sum`与`ui`之间的大小关系。
下面是另一种错误的写法。
```c
unsigned int i = 5;
unsigned int j = 7;
if (i - j < 0) // 错误
printf("negative\n");
else
printf("positive\n");
```
上面示例的运算结果,会输出`positive`。原因是变量`i`和`j`都是 unsigned int 类型,`i - j`的结果也是这个类型,最小值为`0`,不可能得到小于`0`的结果。正确的写法是写成下面这样。
```c
if (j > i) // ....
```
## sizeof 运算符
`sizeof`是 C 语言提供的一个运算符,返回某种数据类型或某个值占用的字节数量。它的参数可以是数据类型的关键字,也可以是变量名或某个具体的值。
```c
// 参数为数据类型
int x = sizeof(int);
// 参数为变量
int i;
sizeof(i);
// 参数为数值
sizeof(3.14);
```
上面的第一个示例,返回得到`int`类型占用的字节数量(通常是`4`或`8`)。第二个示例返回整数变量占用字节数量,结果与前一个示例完全一样。第三个示例返回浮点数`3.14`占用的字节数量,由于浮点数的字面量一律存储为 double 类型,所以会返回`8`,因为 double 类型占用的8个字节。
`sizeof`运算符的返回值C 语言只规定是无符号整数,并没有规定具体的类型,而是留给系统自己去决定,`sizeof`到底返回什么类型。不同的系统中,返回值的类型有可能是`unsigned int`,也有可能是`unsigned long`,甚至是`unsigned long long`,对应的`printf()`占位符分别是`%u`、`%lu`和`%llu`。这样不利于程序的可移植性。
C 语言提供了一个解决方法,创造了一个类型别名`size_t`,用来统一表示`sizeof`的返回值类型。该别名定义在`stddef.h`头文件(引入`stdio.h`时会自动引入)里面,对应当前系统的`sizeof`的返回值类型,可能是`unsigned int`,也可能是`unsigned long`。
C 语言还提供了一个常量`SIZE_MAX`,表示`size_t`可以表示的最大整数。所以,`size_t`能够表示的整数范围为`[0, SIZE_MAX]`。
`printf()`有专门的占位符`%zd`或`%zu`,用来处理`size_t`类型的值。
```c
printf("%zd\n", sizeof(int));
```
上面代码中,不管`sizeof`返回值的类型是什么,`%zd`占位符(或`%zu`)都可以正确输出。
如果当前系统不支持`%zd`或`%zu`,可使用`%u`unsigned int或`%lu`unsigned long int代替。
## 类型的自动转换
某些情况下C 语言会自动转换某个值的类型。
### 赋值运算
赋值运算符会自动将右边的值,转成左边变量的类型。
1浮点数赋值给整数变量
浮点数赋予整数变量时C 语言直接丢弃小数部分,而不是四舍五入。
```c
int x = 3.14;
```
上面示例中,变量`x`是整数类型,赋给它的值是一个浮点数。编译器会自动把`3.14`先转为`int`类型,丢弃小数部分,再赋值给`x`,因此`x`的值是`3`。
这种自动转换会导致部分数据的丢失(`3.14`丢失了小数部分),所以最好不要跨类型赋值,尽量保证变量与所要赋予的值是同一个类型。
注意,舍弃小数部分时,不是四舍五入,而是整个舍弃。
```c
int x = 12.99;
```
上面示例中,`x`等于`12`,而不是四舍五入的`13`。
2整数赋值给浮点数变量
整数赋值给浮点数变量时,会自动转为浮点数。
```c
float y = 12 * 2;
```
上面示例中,变量`y`的值不是`24`,而是`24.0`,因为等号右边的整数自动转为了浮点数。
3窄类型赋值给宽类型
字节宽度较小的整数类型,赋值给字节宽度较大的整数变量时,会发生类型提升,即窄类型自动转为宽类型。
比如,`char`或`short`类型赋值给`int`类型,会自动提升为`int`。
```c
char x = 10;
int i = x + y;
```
上面示例中,变量`x`的类型是`char`,由于赋值给`int`类型,所以会自动提升为`int`。
4宽类型赋值给窄类型
字节宽度较大的类型赋值给字节宽度较小的变量时会发生类型降级自动转为后者的类型。这时可能会发生截值truncation系统会自动截去多余的二进制位导致难以预料的结果。
```javascript
int i = 321;
char ch = i; // ch 的值是 65 321 - 256
```
上面例子中,变量`ch`是`char`类型宽度是8个二进制位。变量`i`是`int`类型,将`i`赋值给`ch`,后者只能容纳`i`(二进制形式为`101000001`共9位的后八位前面多出来的二进制位被丢弃保留后八位就变成了`01000001`十进制的65相当于字符`A`)。
浮点数赋值给整数类型的值,也会发生截值,浮点数的小数部分会被截去。
```c
double pi = 3.14159;
int i = pi; // i 的值为 3
```
上面示例中,`i`等于`3``pi`的小数部分被截去了。
### 混合类型的运算
不同类型的值进行混合计算时,必须先转成同一个类型,才能进行计算。转换规则如下:
1整数与浮点数混合运算时整数转为浮点数类型与另一个运算数类型相同。
```c
3 + 1.2 // 4.2
```
上面示例是`int`类型与`float`类型的混合计算,`int`类型的`3`会先转成`float`的`3.0`,再进行计算,得到`4.2`。
2不同的浮点数类型混合运算时宽度较小的类型转为宽度较大的类型比如`float`转为`double``double`转为`long double`。
3不同的整数类型混合运算时宽度较小的类型会提升为宽度较大的类型。比如`short`转为`int``int`转为`long`等,有时还会将带符号的类型`signed`转为无符号`unsigned`。
下面例子的执行结果,可能会出人意料。
```c
int a = -5;
if (a < sizeof(int)
do_something();
```
上面示例中,变量`a`是带符号整数,`sizeof(int)`是`size_t`类型这是一个无符号整数。按照规则signed int 自动转为 unsigned int所以`a`会自动转成无符号整数`4294967291`(转换规则是`-5`加上无符号整数的最大值再加1导致比较失败`do_something()`不会执行。
所以,最好避免无符号整数与有符号整数的混合运算。因为这时 C 语言会自动将`signed int`转为`unsigned int`,可能不会得到预期的结果。
### 整数类型的运算
两个相同类型的整数运算时,或者单个整数的运算,一般来说,运算结果也属于同一类型。但是有一个例外,宽度小于`int`的类型,运算结果会自动提升为`int`。
```c
unsigned char a = 66;
if ((-a) < 0) printf("negative\n");
else printf("positive\n");
```
上面示例中,变量`a`是 unsigned char 类型这个类型不可能小于0但是`-a`不是 unsigned char 类型,会自动转为 int 类型,导致上面的代码输出 negative。
再看下面的例子。
```c
unsigned char a = 1;
unsigned char b = 255;
unsigned char c = 255;
if ((a - 5) < 0) do_something();
if ((b + c) > 300) do_something();
```
上面示例中,表达式`a - 5`和`b + c`都会自动转为 int 类型,所以函数`do_something()`会执行两次。
### 函数
函数的参数和返回值,会自动转成函数定义里指定的类型。
```c
int dostuff(int, unsigned char);
char m = 42;
unsigned short n = 43;
long long int c = dostuff(m, n);
```
上面示例中,参数变量`m`和`n`不管原来的类型是什么,都会转成函数`dostuff()`定义的参数类型。
下面是返回值自动转换类型的例子。
```c
char func(void) {
int a = 42;
return a;
}
```
上面示例中,函数内部的变量`a`是`int`类型,但是返回的值是`char`类型,因为函数定义中返回的是这个类型。
## 类型的显式转换
原则上应该避免类型的自动转换防止出现意料之外的结果。C 语言提供了类型的显式转换,允许手动转换类型。
只要在一个值或变量的前面,使用圆括号指定类型`(type)`就可以将这个值或变量转为指定的类型这叫做“类型指定”casting
```c
(unsigned char) ch
```
上面示例将变量`ch`转成无符号的字符类型。
```c
long int y = (long int) 10 + 12;
```
上面示例中,`(long int)`将`10`显式转为`long int`类型。这里的显示转换其实是不必要的,因为赋值运算符会自动将右边的值,转为左边变量的类型。
## 可移植类型
C 语言的整数类型short、int、long在不同计算机上占用的字节宽度可能是不一样的无法提前知道它们到底占用多少个字节。
程序员有时控制准确的字节宽度,这样的话,代码可以有更好的可移植性,头文件`stdint.h`创造了一些新的类型别名。
1精确宽度类型(exact-width integer type),保证某个整数类型的宽度是确定的。
- `int8_t`8位有符号整数。
- `int16_t`16位有符号整数。
- `int32_t`32位有符号整数。
- `int64_t`64位有符号整数。
- `uint8_t`8位无符号整数。
- `uint16_t`16位无符号整数。
- `uint32_t`32位无符号整数。
- `uint64_t`64位无符号整数。
上面这些都是类型别名,编译器会指定它们指向的底层类型。比如,某个系统中,如果`int`类型为32位`int32_t`就会指向`int`;如果`long`类型为32位`int32_t`则会指向`long`。
下面是一个使用示例。
```c
#include <stdio.h>
#include <stdint.h>
int main(void) {
int32_t x32 = 45933945;
printf("x32 = %d\n", x32);
return 0;
}
```
上面示例中,变量`x32`声明为`int32_t`类型可以保证是32位的宽度。
2最小宽度类型minimum width type保证某个整数类型的最小长度。
- int_least8_t
- int_least16_t
- int_least32_t
- int_least64_t
- uint_least8_t
- uint_least16_t
- uint_least32_t
- uint_least64_t
上面这些类型,可以保证占据的字节不少于指定宽度。比如,`int_least8_t`表示可以容纳8位有符号整数的最小宽度的类型。
3最快的最小宽度类型fast minimum width type可以使整数计算达到最快的类型。
- int_fast8_t
- int_fast16_t
- int_fast32_t
- int_fast64_t
- uint_fast8_t
- uint_fast16_t
- uint_fast32_t
- uint_fast64_t
上面这些类型是保证字节宽度的同时,追求最快的运算速度,比如`int_fast8_t`表示对于8位有符号整数运算速度最快的类型。这是因为某些机器对于特定宽度的数据运算速度最快举例来说32位计算机对于32位数据的运算速度会快于16位数据。
4可以保存指针的整数类型。
- `intptr_t`:可以存储指针(内存地址)的有符号整数类型。
- `uintptr_t`:可以存储指针的无符号整数类型。
5最大宽度整数类型用于存放最大的整数。
- `intmax_t`:可以存储任何有效的有符号整数的类型。
- `uintmax_t`:可以存放任何有效的无符号整数的类型。
上面的这两个类型的宽度比`long long`和`unsigned long`更大。