前言

之前学习各种编程语言的时候,往往会直接跳过它的数据类型这一章节,因为对于应用层的编写,在这个到处都是多核心多线程,大内存的时代,“浪费”显得理所当然。

在我重新开始学习嵌入式后,我发现内存不再是以 MB,GB 为单位,而是以 B、KB 为单位,那么更加深入的了解数据类型和每个类型的占用内存大小,就很有必要了。


准备

  1. STC89C52 + SDCC
  2. Windows11 + MinGW GCC

C 的数据类型

C 语言只提供了几种基本数据类型,在那些复杂代码中看到的各种数据类型都是基于它们衍生的。

不同的资料对于 C 的数据类型的说法都不尽相同,这是最开始让人很困惑的一点,因为它存在一些范围和底层机器硬件是相关的,不同于 Java 的强数据类型,在 Java 中,整型的范围和运行 Java 代码的机器无关,Java 的每个整型类型,在不同的机器上都是一致的(byte:1 字节,short:2 字节,int:4 字节,long:8 字节),并且 Java 不存在无符号(unsigned)的整型类型,所有整型都是有符号类型。

但在 C 中,整型存在 signed 和 unsigned 的修饰符,导致它们在不同的修饰符下的范围是不同的,并且 short int 和 int 以及 long int 在 K&R 和 ANSI 标准中,只规定了相互的大小,而非具体的字节数,规则如下:

没有 unsigned 或者 signed 修饰的,默认都是有符号(signed)数,char 除外

int 占用字节大于或者等于 short int

long int 占用字节大于或者等于 int

short int 和 int 至少 16 位

long int 至少 32 位

至于 int,它是 16 位还是 32 位,取决于编译器,通常是对应机器最为自然(高性能)的位数

对于类型的分类,每本书都差不多。

K&R:

  1. char,字符型,占用一个字节,存放本地字符集的一个字符。
  2. int,整型,通常反映所用机器整数的最自然长度。
  3. float,单精度浮点型。
  4. double,双精度浮点型。

C 和指针:

  1. 整型
  2. 浮点型
  3. 指针
  4. 聚合类型(数组和结构)
  5. 整型包含 char,short int,int,long int,都有有符号(signed)和无符号(unsigned)两种版本。

整型

char

char 的本质也是整型,虽然目的是表示字符,但是字符在计算机中的本质就是整型数字,缺省的 char,要么是 signed char,要么是 unsigned char,这又又又却决于编译器。

ASCII 是一种编码系统,将拉丁字符映射到整型数字,从 0 到 127,共计 128 个字符。

从二进制的角度来看 127 是 0111 1111,也就是说只要 7 个 bit 就能表示出所有的 ASCII 字符。

如果指定 unsigned 修饰符,那么范围就是 0 - 255,如果是 signed 修饰符,范围就是 -128 - 127,而 ASCII 的范围是 0 - 127,所以当使用 char 来表示,无论是 unsigned 还是 signed 都没问题。

但是 char 也会用来表示普通的整型,这时候,unsigned 和 signed 在移植时就会产生问题,最佳的方案就是把变量的值限制在 unsigned 和 signed 的交集范围内。

字符常量

下面的语句,效果是一样的:

char c1 = 'A';
char c2 = 65;

这里的 'A' 就是字符常量,神奇的是,字符常量的类型总是 int,也就是说,'A' 对应的 65 是存储在 32 位(或者 16 位)的存储单元中,但是赋值的变量是 char 类型,32 位或者 16 位的字符常量存到 8 位的 char 类型的变量后,高位会被截掉,仅仅保留下了低 8 位(1 字节)的内容。

但在字符常量存储到 char 类型变量的截断,最高位是用不到的,在 0 - 127 之间,最高位永远都是 0,并不会引起符号的问题。

符号扩展

但当存储超过 127 的数值时,比如下面的代码,尽管二进制层面是一致的,但是解释的值却不同:

char c1 = 0xFF;
unsigned char c2 = c1;
signed char c3 = c1;

// 我的笔记本上的结果是:
// c1: -1
// c2: 255
// c3: -1
printf("c1: %d\nc2: %d\nc3: %d\n", c1, c2, c3);

这里涉及到一个符号扩展的问题,我的机器上 char 默认是有符号的,所以 c1 和 c3 保持一致,在二进制为 1111 1111 中,都将最高位作为符号位,解释成了 -1 这个值,但在 unsigned char 中,没有把它作为符号位,所以解释成了 255。

int