命名空间
变体
操作

存储类说明符

来自 cppreference.cn
< c‎ | language

指定对象和函数的存储期链接

  • auto - 自动存储期和无链接
  • register - 自动存储期和无链接;无法获取此变量的地址
  • static - 静态存储期和内部链接(除非在块作用域)
  • extern - 静态存储期和外部链接(除非已声明为内部链接)
  • _Thread_local(直到 C23)thread_local(自 C23 起) - 线程存储期
(自 C11 起)

目录

[编辑] 解释

存储类说明符出现在声明复合字面量表达式(自 C23 起)中。最多可以使用一个说明符,但 _Thread_local(直到 C23)thread_local(自 C23 起) 可以与 staticextern 结合使用以调整链接(自 C11 起)。存储类说明符确定它们声明的名称的两个独立属性:存储期链接

1) auto 说明符仅允许用于在块作用域中声明的对象(函数参数列表除外)。它表示自动存储期和无链接,这是这些类型的声明的默认设置。
2) register 说明符仅允许用于在块作用域中声明的对象,包括函数参数列表。它表示自动存储期和无链接(这是这些类型的声明的默认设置),但另外提示优化器尽可能将此变量的值存储在 CPU 寄存器中。无论是否进行此优化,声明为 register 的变量都不能用作取地址运算符的参数,不能使用 _Alignas(直到 C23)alignas(自 C23 起)(自 C11 起),并且 register 数组不能转换为指针。
3) static 说明符指定静态存储期(除非与 _Thread_local 结合使用)(自 C11 起)和内部链接(除非在块作用域中使用)。它可用于文件作用域中的函数以及文件和块作用域中的变量,但不能用于函数参数列表。
4) extern 说明符指定静态存储期(除非与 _Thread_local(直到 C23)thread_local(自 C23 起) 结合使用)(自 C11 起)和外部链接。它可用于文件和块作用域(不包括函数参数列表)中的函数和对象声明。如果 extern 出现在已声明为内部链接的标识符的重新声明中,则链接保持为内部链接。否则(如果先前的声明是外部链接、无链接或不在作用域中),则链接是外部链接。
5) _Thread_local(直到 C23)thread_local(自 C23 起) 指示线程存储期。它不能用于函数声明。如果它用于对象的声明,则必须在同一对象的每个声明中都存在。如果它用于块作用域声明,则必须与 staticextern 结合使用以决定链接。
(自 C11 起)

如果未提供存储类说明符,则默认值为

extern 用于所有函数
extern 用于文件作用域中的对象
auto 用于块作用域中的对象

对于使用存储类说明符声明的任何结构体或联合体,存储期(但不是链接)递归地应用于其成员。

块作用域中的函数声明可以使用 extern 或不使用任何说明符。文件作用域中的函数声明可以使用 externstatic

函数参数不能使用除 register 之外的任何存储类说明符。请注意,static 在数组类型的函数参数中具有特殊含义。

[编辑] 存储期

每个对象都具有一个称为存储期的属性,该属性限制了对象的生命周期。C 语言中有四种存储期

  • 自动存储期。当进入声明对象的时分配存储空间,并在通过任何方式(gotoreturn、到达末尾)退出该块时释放存储空间。一个例外是变长数组 (VLA);它们的存储空间在执行声明时分配,而不是在块入口时分配,并且在声明超出作用域时释放,而不是在块退出时释放(自 C99 起)。如果递归进入块,则为每个递归级别执行新的分配。所有函数参数和非 static 块作用域对象都具有此存储期,以及在块作用域中使用的复合字面量(直到 C23)
  • 静态存储期。存储期是程序的整个执行过程,并且存储在对象中的值仅初始化一次,在main 函数之前。所有声明为 static 的对象以及具有内部或外部链接的对象且未声明为 _Thread_local(直到 C23)thread_local(自 C23 起)(自 C11 起)都具有此存储期。
  • 线程存储期。存储期是创建它的线程的整个执行过程,并且存储在对象中的值在线程启动时初始化。每个线程都有其自己的、不同的对象。如果执行访问此对象的表达式的线程不是执行其初始化的线程,则行为是实现定义的。所有声明为 _Thread_local(直到 C23)thread_local(自 C23 起) 的对象都具有此存储期。
(自 C11 起)
  • 已分配存储期。存储空间根据请求使用动态内存分配函数分配和释放。

[编辑] 链接

链接是指标识符(变量或函数)在其他作用域中被引用的能力。如果在多个作用域中声明了具有相同标识符的变量或函数,但不能从所有作用域引用它们,则会生成变量的多个实例。识别以下链接

  • 无链接。变量或函数只能从其所在的作用域(块作用域)引用。所有未声明为 extern 的块作用域变量都具有此链接,以及所有函数参数和所有非函数或变量的标识符。
  • 内部链接。变量或函数可以从当前翻译单元中的所有作用域引用。所有声明为 static constexpr(自 C23 起) 的文件作用域变量都具有此链接,以及所有声明为 static 的文件作用域函数(静态函数声明仅允许在文件作用域中)。
  • 外部链接。变量或函数可以从整个程序中的任何其他翻译单元引用。所有未声明为 staticconstexpr(自 C23 起) 的文件作用域变量都具有此链接,所有未声明为 static 的文件作用域函数声明,所有块作用域函数声明,以及另外,所有声明为 extern 的变量或函数都具有此链接,除非在该点可见具有内部链接的先前声明。

如果同一标识符在同一翻译单元中同时具有内部链接和外部链接,则行为未定义。当使用尝试性定义时,可能会发生这种情况。

[编辑] 链接和库

具有外部链接的声明通常在头文件中提供,以便所有 #include 该文件的翻译单元都可以引用在其他地方定义的同一标识符。

出现在头文件中的任何具有内部链接的声明都会在包含该文件的每个翻译单元中产生一个单独且不同的对象。

库接口,头文件 "flib.h"

#ifndef FLIB_H
#define FLIB_H
void f(void);              // function declaration with external linkage
extern int state;          // variable declaration with external linkage
static const int size = 5; // definition of a read-only variable with internal linkage
enum { MAX = 10 };         // constant definition
inline int sum (int a, int b) { return a + b; } // inline function definition
#endif // FLIB_H

库实现,源文件 "flib.c"

#include "flib.h"
 
static void local_f(int s) {} // definition with internal linkage (only used in this file)
static int local_state;       // definition with internal linkage (only used in this file)
 
int state;                       // definition with external linkage (used by main.c)
void f(void) { local_f(state); } // definition with external linkage (used by main.c)

应用程序代码,源文件 "main.c"

#include "flib.h"
 
int main(void)
{
    int x[MAX] = {size}; // uses the constant and the read-only variable
    state = 7;           // modifies state in flib.c
    f();                 // calls f() in flib.c
}

[编辑] 关键字

auto, register, static, extern, _Thread_local thread_local

[编辑] 注释

关键字 _Thread_local 通常通过头文件 <threads.h> 中定义的便利宏 thread_local 使用。

(直到 C23)

typedefconstexpr(自 C23 起) 说明符在 C 语言语法中被正式列为存储类说明符,但不指定存储。

auto 说明符也用于类型推断。

(自 C23 起)

文件作用域中声明为 const 且非 extern 的名称在 C 语言中具有外部链接(作为所有文件作用域声明的默认设置),但在 C++ 中具有内部链接。

[编辑] 示例

#include <stdio.h>
#include <stdlib.h>
 
// static storage duration
int A;
 
int main(void)
{
    printf("&A = %p\n", (void*)&A);
 
    // automatic storage duration
    int A = 1;   // hides global A
    printf("&A = %p\n", (void*)&A);
 
    // allocated storage duration
    int* ptr_1 = malloc(sizeof(int));   // start allocated storage duration
    printf("address of int in allocated memory = %p\n", (void*)ptr_1);
    free(ptr_1);                        // stop allocated storage duration
}

可能的输出

&A = 0x600ae4
&A = 0x7ffefb064f5c
address of int in allocated memory = 0x1f28c30

[编辑] 参考文献

  • C23 标准 (ISO/IEC 9899:2024)
  • 6.2.2 标识符的链接 (p: 35-36)
  • 6.2.4 对象的存储期 (p: 36-37)
  • 6.7.1 存储类说明符 (p: 97-100)
  • C17 标准 (ISO/IEC 9899:2018)
  • 6.2.2 标识符的链接 (p: 29-30)
  • 6.2.4 对象的存储期 (p: 30)
  • 6.7.1 存储类说明符 (p: 79)
  • C11 标准 (ISO/IEC 9899:2011)
  • 6.2.2 标识符的链接 (p: 36-37)
  • 6.2.4 对象的存储期 (p: 38-39)
  • 6.7.1 存储类说明符 (p: 109-110)
  • C99 标准 (ISO/IEC 9899:1999)
  • 6.2.2 标识符的链接 (p: 30-31)
  • 6.2.4 对象的存储期 (p: 32)
  • 6.7.1 存储类说明符 (p: 98-99)
  • C89/C90 标准 (ISO/IEC 9899:1990)
  • 3.1.2.2 标识符的链接
  • 3.1.2.4 对象的存储期
  • 3.5.1 存储类说明符

[编辑] 参见

C++ 文档 关于 存储类说明符