类型基础
类型基础
作为程序员,我们熟悉使用变量来表示存储值的内存区域。在诸如 C 的类型化语言中,每个变量都必须使用类型声明。该类型告诉编译器我们期望在变量中存储的内容。然后,编译器既可以为此使用分配足够的空间,又可以检查程序员是否没有违反该类型的规则。在下面的示例中,我们看到为某些常见类型的变量分配空间的示例。
C99 标准只提到了为 C 定义的每种类型的最小尺寸。这是因为在不同的处理器体系结构和操作系统中,用于类型的最佳尺寸可能会千差万别。为了完全安全,程序员永远不必假设任何变量的大小,但是,一个正常运行的系统显然需要就系统中将使用的大小类型达成协议。每个体系结构和操作系统都符合应用程序二进制接口或 ABI。系统的 ABI 会在 C 标准与基础硬件和操作系统的要求之间填写详细信息。为特定的处理器和操作系统组合编写了 ABI。
Type | C99 minimum size (bits) | Common size (32 bit architecture) |
---|---|---|
char |
8 | 8 |
short |
16 | 16 |
int |
16 | 32 |
long |
32 | 32 |
long long |
64 | 64 |
Pointers | Implementation dependent | 32 |
上面我们可以看到,与标准的唯一差异是 int 通常为 32 位,这是 C99 要求的最低 16 位的严格大小的两倍。指针实际上只是一个地址(即它们的值是一个地址,因此“指向”内存中的其他位置),因此指针需要足够的大小才能寻址系统中的任何内存。
64 bit
首先,这意味着所有指针必须为 64 位宽,以便它们可以表示系统中任何可能的地址。但是,系统实现者必须随后决定其他类型的大小。如下所示,广泛使用了两种常见的模型。
Type | C99 minimum size (bits) | Common size (LP64) | Common size (Windows) |
---|---|---|---|
char |
8 | 8 | 8 |
short |
16 | 16 | 16 |
int |
16 | 32 | 32 |
long |
32 | 64 | 32 |
long long |
64 | 64 | 64 |
Pointers | Implementation dependent | 64 | 64 |
您可以看到,在 LP64(长指针 64)模型中,长值定义为 64 位宽。这与我们之前显示的 32 位模型不同。LP64 模型广泛用于 UNIX 系统。在另一个模型中,long 保留为 32 位值。这样可以保持与 32 码的最大兼容性。该模型与 64 位 Windows 一起使用。在两个模型中 int 的大小都没有增加到 64 位的理由很充分。考虑到如果将 int 的大小增加到 64 位,则程序员将无法获得 32 位变量。唯一可能的是将短裤重新定义为较大的 32 位类型。
64 位变量太大,以至于通常不需要代表许多变量。例如,循环很少会重复超过 32 位变量(4294967296 次!)所能容纳的次数。通常,对于红色,绿色和蓝色值,通常使用 8 位表示图像,对于额外的(alpha 通道)信息,通常使用 8 位表示。总共 32 位。因此,在许多情况下,使用 64 位变量将浪费至少前 32 位(如果不是更多的话)。不仅如此,整数数组的大小现在也增加了一倍。这意味着程序将占用更多的系统内存(从而占用更多的缓存;在第 3 章,计算机体系结构中将详细讨论),而没有真正的改进。出于同样的原因,Windows 选择将其 long 值保留为 32 位。由于大多数 Windows API 最初是为在 32 位系统上使用长变量而编写的,因此不需要额外的位,因此可以节省系统中的大量浪费空间,而无需重新编写所有 API。
如果我们考虑提议的替代方法,其中 short 被重新定义为 32 位变量;在 64 位系统上工作的程序员可以将其用于他们知道限制在较小值上的变量。但是,当移回 32 位系统时,它们相同的 short 变量现在只有 16 位长,这个值实际上要溢出得多(65536)。通过让程序员在知道需要大的变量时请求更大的变量,就可移植性问题和浪费二进制空间达成了平衡。
类型限定符(Type Qualifiers)
C 标准还讨论了变量类型的一些限定符例如 const 意味着一个变量将永远不会从其原始值被修改,而 volatile 则向编译器暗示该值可能会在程序执行流程之外发生变化,因此编译器必须注意不要以任何方式对访问进行重新排序。带符号和不带符号可能是两个最重要的限定词他们说变量是否可以取负值我们将在下面对此进行更详细的研究。
限定符都旨在将有关如何将变量用于编译器的额外信息这意味着两件事;编译器可以检查您是否违反了自己的规则(例如,写入 const 值),并且可以基于额外的知识进行优化(在后面的章节中进行了检查)。
标准类型
C99 意识到所有这些规则,大小和可移植性问题很快就会变得非常混乱为了提供帮助,它提供了一系列特殊类型,可以指定变量的确切属性它们在<stdint.h>
中定义,格式为 qtypes_t,其中 q 是限定符,type 是基本类型,s 是位宽度,_t
是扩展名,因此您知道您正在使用 C99 定义的类型。
因此,例如 uint8_t 是正好为 8 位宽的无符号整数定义了许多其他类型完整列表在 C99 17.8 或更详细(在头文件中)详细描述取决于实现 C99 标准的系统,您可以通过将它们映射到目标系统上适当大小的类型来为您提供这些类型在 Linux 上,这些标头由系统库提供。