02-存储类别
12.1 存储类别
C提供了多种不同的模型或存储类别(storage class)在内存中存储数据。要理解这些存储类别,先要复习一些概念和术语。
本书目前所有编程示例中使用的数据都存储在内存中。从硬件方面来看,被存储的每个值都占用一定的物理内存,C语言把这样的一块内存称为对象(object)。对象可以存储一个或多个值。一个对象可能并未存储实际的值,但是它在存储适当的值时一定具有相应的大小(面向对象编程中的对象指的是类对象,其定义包括数据和允许对数据进行的操作,C不是面向对象编程语言)。
从软件方面来看,程序需要一种方法访问对象。这可以通过声明变量来完成:
int entity = 3;
该声明创建了一个名为 entity
的标识符(identifier)。标识符是一个名称,在这种情况下,标识符可以用来指定(designate)特定对象的内容。标识符遵循变量的命名规则(第2章介绍过)。在该例中,标识符 entity
即是软件(即C程序)指定硬件内存中的对象的方式。该声明还提供了存储在对象中的值。
变量名不是指定对象的唯一途径。考虑下面的声明:
int * pt = &entity;
int ranks[10];
第1行声明中, pt
是一个标识符,它指定了一个存储地址的对象。但是,表达式 pt
不是标识符,因为它不是一个名称。然而,它确实指定了一个对象,在这种情况下,它与 entity
指定的对象相同。一般而言,那些指定对象的表达式被称为左值(第5章介绍过)。所以, entity
既是标识符也是左值; pt
既是表达式也是左值。按照这个思路, ranks + 2
entity
既不是标识符(不是名称),也不是左值(它不指定内存位置上的内容)。但是表达式 (ranks + 2
* entity)
是一个左值,因为它的确指定了特定内存位置的值,即 ranks
数组的第7个元素。顺带一提, ranks
的声明创建了一个可容纳10个 int
类型元素的对象,该数组的每个元素也是一个对象。
所有这些示例中,如果可以使用左值改变对象中的值,该左值就是一个可修改的左值(modifiable lvalue)。现在,考虑下面的声明:
const char * pc = "Behold a string literal!";
程序根据该声明把相应的字符串字面量存储在内存中,内含这些字符值的字符串字面量就是一个对象。由于字符串字面量中的每个字符都能被单独访问,所以每个字符也是一个对象。该声明还创建了一个标识符为 pc
的对象,存储着字符串的地址。由于可以设置 pc
重新指向其他字符串,所以标识符 pc
是一个可修改的左值。 const
只能保证被 pc
指向的字符串内容不被修改,但是无法保证 pc
不指向别的字符串。由于 pc
指定了存储 'B'
字符的数据对象,所以 pc
是一个左值,但不是一个可修改的左值。与此类似,因为字符串字面量本身指定了存储字符串的对象,所以它也是一个左值,但不是可修改的左值。
可以用存储期(storage duration)描述对象,所谓存储期是指对象在内存中保留了多长时间。标识符用于访问对象,可以用作用域(scope)和链接(linkage)描述标识符,标识符的作用域和链接表明了程序的哪些部分可以使用它。不同的存储类别具有不同的存储期、作用域和链接。标识符可以在源代码的多文件中共享、可用于特定文件的任意函数中、可仅限于特定函数中使用,甚至只在函数中的某部分使用。对象可存在于程序的执行期,也可以仅存在于它所在函数的执行期。对于并发编程,对象可以在特定线程的执行期存在。可以通过函数调用的方式显式分配和释放内存。
我们先学习作用域、链接和存储期的含义,再介绍具体的存储类别。