存储类指定符
指定对象和函数的存储期( storage duration )和链接( linkage ):
auto
- 自动存储期与无链接register
- 自动存储期与无链接;不能取这种对象的地址static
- 静态存储期与内部链接(除非在块作用域)extern
- 静态存储期与外部链接(除非已声明带内部链接)
|
(C11 起) |
解释
存储类指定符出现于声明中。至多可使用一个指定符,除了能将 _Thread_local
与 static
或 extern
组合以调整链接 (C11 起)。存储类指定符确定其所声明的名称的二个独立属性:存储期与链接。
auto
指定符只对声明于块作用域的对象(除了函数参数列表)允许。它指示自动存储期与无链接,也是这种声明的默认属性。register
指定符只对声明于块作用域的对象允许,包括函数参数列表。它指示自动存储期与无链接(即这种声明的默认属性),但另外提示优化器,若可能则将此对象的值存储于 CPU 寄存器中。无论此优化是否发生,声明为 register
的对象不能用作取址运算符的参数,不能用 _Alignas (C11 起),而且 register
数组不能转换为指针。static
指定符指定静态存储期(除非与 _Thread_local
组合) (C11 起)和内部链接(除非用于块作用域)。它能用于在文件作用域的函数,以及文件和块作用域的对象,但不能用于函数参数列表。extern
指定静态存储期(除非与 _Thread_local
组合) (C11 起)和外部链接。它能用于文件和块作用域中的函数和对象声明(除了函数参数列表)。若 extern
出现在已经声明带内部链接的标识符的再声明上,则链接仍为内部。否则(若前一声明为外部、无链接或不在作用域内)链接为外部。
5)
_Thread_local 指示线程存储期。它不能用于函数声明。若将它用在对象声明上,则它必须在同一对象的每次声明上都存在。若将它用在块作用域声明上,则必须与 static 或 extern 之一组合以决定链接。 |
(C11 起) |
若不提供存储类指定符,则默认为:
- 对所有函数为
extern
- 对在文件作用域的对象为
extern
- 对在块作用域的对象为
auto
对于任何用存储类指定符声明的结构体或联合体,存储期(但非链接)递归地应用到其成员。
在块作用域的函数声明能使用 extern
或完全不使用存储类指定符。在文件作用域的函数声明能使用 extern
或 static
。
函数参数不能使用异于 register
的存储类指定符。注意 static
在数组类型的函数参数中有特殊含义。
存储期
每个对象都有称为存储期的属性,它限制对象的生存期。 C 中有四种存储期:
- 静态存储期。存储期是整个程序的执行过程,只在
main
函数之前初始化一次存储于对象的值。所有声明为static
对象和所有带内部或外部链接且不声明为_Thread_local
(C11 起)的对象都拥有此存储期。
- 静态存储期。存储期是整个程序的执行过程,只在
|
(C11 起) |
- 分配存储期。按照请求,用动态内存分配函数分配和解分配存储。
链接
链接指的是在其他作用域指代一个标识符(具名对象或函数)的能力。若在数个作用域中声明有同一标识符的对象或函数,但不能从所有这些作用域指代它们,则会创建数个对象的实例。辨识下列链接:
- 无链接。只能从其所在的作用域指代该标识符。所有函数参数和所有非
extern
的块作用域对象(包含声明为static
者)拥有此链接。
- 无链接。只能从其所在的作用域指代该标识符。所有函数参数和所有非
- 内部链接。能从当前翻译单元的所有作用域指代该标识符。所有
static
文件作用域标识符(函数和对象)都拥有此链接。
- 内部链接。能从当前翻译单元的所有作用域指代该标识符。所有
- 外部链接。能从整个程序的任何其他翻译单元指代该标识符。所有非
static
函数、所有extern
对象(除非之前声明为static
)和所有文件作用域的非static
对象拥有此链接。
- 外部链接。能从整个程序的任何其他翻译单元指代该标识符。所有非
若同一标识符在同一翻译单元中一同带内部和外部链接出现,则行为未定义。这在使用试探性定义时有可能。
链接与库
本节未完成 原因:这或许应该为 c/language 中杂项下的顶层条目? |
带外部链接的声明常在头文件中可用,这使得所有 #include 该头文件的翻译单元都可以指代定义于别处的相同标识符。
任何出现于头文件中的带内部链接的声明,在每个包含该文件的翻译单元中产生一个分离而相异的对象或函数。
库接口:
// flib.h #ifndef FLIB_H #define FLIB_H void f(void); // 带外部链接的函数声明 extern int state; // 带外部链接的对象声明 static const int size = 5; // 带内部链接的只读对象定义 enum { MAX = 10 }; // 常量定义 inline int sum (int a, int b) { return a+b; } // inline 函数定义 #endif // FLIB_H
库实现:
// flib.c #include "flib.h" static void local_f(int s) {} // 带内部链接的定义(只用于此文件) static int local_state; // 带内部链接的定义(只用于此文件) int state; // 带外部链接的定义( main.c 使用) void f(void) {local_f(state);} // 带外部链接的定义( main.c 使用)
应用代码:
// main.c #include "flib.h" int main(void) { int x[MAX] = {size}; // 使用常量和只读变量 state = 7; // 修改 flib.c 中的 state f(); // 调用 flib.c 中的 f() }
关键词
auto, register, static, extern, _Thread_local
注意
一般通过定义于头文件 threads.h
的便利宏 thread_local 使用关键词 _Thread_local
。
C 语言文法中, typedef 指定符在形式上列作存储类指定符,但它被用于声明类型名,而不指定存储。
在文件作用域的 const
且非 extern
的名称在 C 中拥有外部链接(同所有文件作用域的默认情况),但在 C++ 中拥有内部链接。
示例
#include <stdio.h> #include <stdlib.h> int A; // 静态存储期 int main(void) { printf("&A = %p\n", (void*)&A); // 自动存储期 int A = 1; // 隐藏全局 A printf("&A = %p\n", (void*)&A); // 分配存储期 int *ptr_1 = malloc(sizeof(int)); // 开始分配存储期 printf("address of int in allocated memory = %p\n", (void*)ptr_1); free(ptr_1); // 停止分配存储期 }
可能的输出:
&A = 0x600ae4 &A = 0x7ffefb064f5c address of int in allocated memory = 0x1f28c30
引用
- C11 standard (ISO/IEC 9899:2011):
- 6.2.2 Linkages of identifiers (p: 36-37)
- 6.2.4 Storage durations of objects (p: 38-39)
- 6.7.1 Storage-class specifiers (p: 109-110)
- C99 standard (ISO/IEC 9899:1999):
- 6.2.2 Linkages of identifiers (p: 30-31)
- 6.2.4 Storage durations of objects (p: 32)
- 6.7.1 Storage-class specifiers (p: 98-99)
- C89/C90 standard (ISO/IEC 9899:1990):
- 3.1.2.2 Linkages of identifiers
- 3.1.2.4 Storage durations of objects
- 3.5.1 Storage-class specifiers