存储类
存储类说明符是可以出现在声明的顶级类型旁边的关键字。这些关键字的使用会影响已声明对象的存储持续时间和链接,具体取决于它是在文件范围还是在块范围内声明:
关键词 | 存储持续时间 | 连锁 | 备注 |
---|---|---|---|
static |
静态的 | 内部 | 为文件范围内的对象设置内部链接; 为块范围内的对象设置静态存储持续时间。 |
extern |
静态的 | 外部 | 对于在文件范围中定义的对象(也具有初始化程序)隐含且因此是冗余的。当在没有初始化程序的文件范围的声明中使用时,提示该定义将在另一个翻译单元中找到,并将在链接时解析。 |
auto |
自动 | 不相干 | 隐含,因此对于在块范围内声明的对象是多余的。 |
register |
自动 | 不相干 | 仅与具有自动存储持续时间的对象相关。提供变量应存储在寄存器中的提示。强加的约束是不能在这样的对象上使用一元 & 地址运算符,因此该对象不能被别名化。 |
typedef |
不相干 | 不相干 | 在实践中不是存储类说明符,但从语法的角度来看就像一个。唯一的区别是声明的标识符是一个类型,而不是一个对象。 |
_Thread_local |
线 | 内部外部 | 在 C11 中引入,表示线程存储持续时间。如果在块范围内使用,它还应包括 extern 或 static 。 |
每个对象都有一个相关的存储持续时间(无论范围如何)和链接(仅与文件范围内的声明相关),即使省略了这些关键字也是如此。
关于顶级类型说明符(int
,unsigned
,short
等)和顶级类型限定符(const
,volatile
)的存储类说明符的顺序未强制执行,因此这两个声明都是有效的:
int static const unsigned a = 5; /* bad practice */
static const unsigned int b = 5; /* good practice */
但是,将存储类说明符放在首位,然后是任何类型限定符,然后是类型说明符(void
,char
,int
,signed long
,unsigned long long
,long 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
)声明。它们通常在程序启动时由操作系统分配和初始化,并在进程终止时回收。在实践中,可执行格式具有用于这些变量的专用部分(data
,bss
和 rodata
),并且来自文件的这些整个部分被映射到特定范围的存储器中。
线程存储持续时间
Version => C11
此存储持续时间在 C11 中引入。这在早期的 C 标准中是不可用的。一些编译器提供具有类似语义的非标准扩展。例如,gcc 支持 __thread
说明符,可以在早期的 C 标准中使用,它没有 _Thread_local
。
具有线程存储持续时间的变量可以在文件范围和块范围内声明。如果在块范围内声明,它还应使用 static
或 extern
存储说明符。它的生命周期是整个执行它创建它的线程。这是唯一可以与另一个存储说明符一起出现的存储说明符。
自动存储持续时间
具有自动存储持续时间的变量只能在块范围内声明(直接在函数内或在该函数的块内)。它们仅在进入和离开功能或块之间的时间段内可用。一旦变量超出范围(通过从函数返回或离开块),它的存储将自动解除分配。从指针对同一变量的任何进一步引用都是无效的,并导致未定义的行为。
在典型的实现中,自动变量位于函数的堆栈帧中或寄存器中的某些偏移处。
外部和内部联系
链接仅与在文件范围内声明的对象(函数和变量)相关,并影响它们在不同翻译单元中的可见性。具有外部链接的对象在每个其他翻译单元中都可见(前提是包含适当的声明)。具有内部链接的对象不会暴露给其他翻译单元,只能在定义它们的翻译单元中使用。