存储类

存储类说明符是可以出现在声明的顶级类型旁边的关键字。这些关键字的使用会影响已声明对象的存储持续时间和链接,具体取决于它是在文件范围还是在块范围内声明:

关键词 存储持续时间 连锁 备注
static 静态的 内部 为文件范围内的对象设置内部链接; 为块范围内的对象设置静态存储持续时间。
extern 静态的 外部 对于在文件范围中定义的对象(也具有初始化程序)隐含且因此是冗余的。当在没有初始化程序的文件范围的声明中使用时,提示该定义将在另一个翻译单元中找到,并将在链接时解析。
auto 自动 不相干 隐含,因此对于在块范围内声明的对象是多余的。
register 自动 不相干 仅与具有自动存储持续时间的对象相关。提供变量应存储在寄存器中的提示。强加的约束是不能在这样的对象上使用一元 &地址运算符,因此该对象不能被别名化。
typedef 不相干 不相干 在实践中不是存储类说明符,但从语法的角度来看就像一个。唯一的区别是声明的标识符是一个类型,而不是一个对象。
_Thread_local 线 内部外部 在 C11 中引入,表示线程存储持续时间。如果在块范围内使用,它还应包括 externstatic

每个对象都有一个相关的存储持续时间(无论范围如何)和链接(仅与文件范围内的声明相关),即使省略了这些关键字也是如此。

关于顶级类型说明符(intunsignedshort 等)和顶级类型限定符(constvolatile)的存储类说明符的顺序未强制执行,因此这两个声明都是有效的:

int static const unsigned a = 5; /* bad practice */
static const unsigned int b = 5; /* good practice */

但是,将存储类说明符放在首位,然后是任何类型限定符,然后是类型说明符(voidcharintsigned longunsigned long longlong double ……),这被认为是一种好习惯。

并非所有存储类说明符在某个范围内都是合法的:

register int x; /* legal at block scope, illegal at file scope */
auto int y; /* same */

static int z; /* legal at both file and block scope */
extern int a; /* same */

extern int b = 5; /* legal and redundant at file scope, illegal at block scope */

/* legal because typedef is treated like a storage class specifier syntactically */
int typedef new_type_name;

存储持续时间

存储持续时间可以是静态的或自动的。对于声明的对象,根据其范围和存储类说明符确定。

静态存储持续时间

具有静态存储持续时间的变量在整个程序执行期间都存在,并且可以在文件范围(有或没有 static)和块范围(通过明确地放置 static)声明。它们通常在程序启动时由操作系统分配和初始化,并在进程终止时回收。在实践中,可执行格式具有用于这些变量的专用部分(databssrodata),并且来自文件的这些整个部分被映射到特定范围的存储器中。

线程存储持续时间

Version => C11

此存储持续时间在 C11 中引入。这在早期的 C 标准中是不可用的。一些编译器提供具有类似语义的非标准扩展。例如,gcc 支持 __thread 说明符,可以在早期的 C 标准中使用,它没有 _Thread_local

具有线程存储持续时间的变量可以在文件范围和块范围内声明。如果在块范围内声明,它还应使用 staticextern 存储说明符。它的生命周期是整个执行它创建它的线程。这是唯一可以与另一个存储说明符一起出现的存储说明符。

自动存储持续时间

具有自动存储持续时间的变量只能在块范围内声明(直接在函数内或在该函数的块内)。它们仅在进入和离开功能或块之间的时间段内可用。一旦变量超出范围(通过从函数返回或离开块),它的存储将自动解除分配。从指针对同一变量的任何进一步引用都是无效的,并导致未定义的行为。

在典型的实现中,自动变量位于函数的堆栈帧中或寄存器中的某些偏移处。

外部和内部联系

链接仅与在文件范围内声明的对象(函数和变量)相关,并影响它们在不同翻译单元中的可见性。具有外部链接的对象在每个其他翻译单元中都可见(前提是包含适当的声明)。具有内部链接的对象不会暴露给其他翻译单元,只能在定义它们的翻译单元中使用。