存储类指定符

来自cppreference.com
< c‎ | language

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

  • auto - 自动存储期且无链接
  • register - 自动存储期且无链接;该变量不可取地址
  • static - 静态存储期及内部链接(除非在块作用域)
  • extern - 静态存储期及外部链接(除非已声明为内部)
  • _Thread_local - 线程局域存储期
(C11起)

目录

[编辑] 解释

存储类指定符出现于声明。最多只允许使用一个指定符,例外是_Thread_local可以与staticextern结合以调节链接 (C11起)。存储类指定符决定两个它们所声明名称的属性:存储期链接

1) auto指定符仅允许声明于块作用域内的对象(除了函数参数列表)。它指示自动存储期及无链接,这是此类声明的默认设定。
2) register指定符仅允许声明于块作用域内的对象,包括函数参数列表。它指示自动存储期及无链接(这是此类声明的默认设定),但附加地提示优化器尽可能地将此变量存储于CPU寄存器中。不管此优化是否执行,声明有register的变量不能用作取址运算符的参数,亦不能使用_Alignas (C11起),而且register数组不能转换成指针。
3) static指定静态存储期(除非与_Thread_local组合) (C11起)及内部链接(除非在块作用域内)。它可用于文件作用域中的函数,和文件及块作用域中的变量,但不可用于函数参数列表。
4) extern指定符指定静态存储期(除非与_Thread_local组合) (C11起)及外部链接。它可用于函数及声明于文件和块作用域中的对象(不含函数参数列表)。若extern出现于一个已声明带内部链接标识符的再声明,则该链接保持为内部。否则(若前面的声明为外部、无链接或不在作用域内),则链接为外部的。
5) _Thread_local指示线程存储期。它不可用于函数声明。若将其用于对象声明,则同一对象的每个声明都必须有它。若它用于块作用域上,则它必须与staticextern结合使用以决定链接。
(C11起)

若不提供存储类指定符,则默认设定是:

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

对于任何声明有存储类指定符的结构体或联合体,存储期(而非链接)递归地作用于其成员。

块作用域的函数声明可以使用extern或不使用存储类指定符。文件作用域的函数声明可使用externstatic

函数参数不可使用任何异于register的存储类指定符。注意static在数组类型的函数参数中拥有特殊含义。

[编辑] 存储期

每个对象拥有名为存储期的属性,它限制对象的生存期。C中有四种存储期:

  • 自动存储期。该存储在进入声明对象的时分配,并在以任意方式(gotoreturn、到达结尾)退出该块时解除分配。一个例外是VLA;它们的存储在执行声明时分配,而非进入块,并在声明离开作用域时解除分配,而非在退出块时 (C99起)。若递归地进入块,则在每一层次的递归执行新的分配。所有函数参数及非static的块作用域对象拥有此存储期,用于块作用域的复合字面量亦如此。
  • 静态存储期。该存储期是整个程序的执行期,而且存储于该对象的值只被初始化一次,先于主函数。所有声明为static的对象及所有拥有内部或外部链接且没有声明为_Thread_local (C11起)的对象拥有此存储期。
  • 线程存储期。该存储期是创建该对象的整个线程的执行期,而且存储于该对象的值在线程开始时别初始化。每个线程拥有它自身的分离的对象。若执行访问此对象的表达式的线程不是执行该对象初始化的线程,则行为是实现定义的。所有声明为_Thread_local的对象拥有此存储期。
(C11起)
  • 分配存储期。此存储期按要求分配及解除分配,通过动态内存分配函数。

[编辑] 链接

链接表示一个标识符(变量或函数)在其他作用域被引用的能力。若拥有相同标识符的变量或函数在数个作用域中声明,但不能被它们全体所引用,则会生成数个变量的实例。下列链接会得到辨认:

  • 无链接。该标识符只能在其所在的作用域内被引用。所有函数参数及所有非extern的块作用域变量(包含声明为static者)拥有此链接。
  • 内部链接。该标识符可以在当前翻译单元的所有作用域内被引用。所有static标识符(函数及变量)都拥有此链接。
  • 外部链接。该标识符可以从整个程序的任何其他翻译单元引用。所有非static函数,所有extern变量(除非先前声明为static),及所有文件作用域的非static变量拥有此链接。

若同一标识符在同一翻译单元内同时出现有内部和外部链接,则行为未定义。这在使用试探性定义时可能出现。

[编辑] 链接与库

拥有外部链接的声明通常在头文件中被设为可用,故所有#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中的状态
    f();                 // 调用flib.c中的f()
}

[编辑] 关键词

auto, register, static, extern, _Thread_local

[编辑] 注意

关键词_Thread_local通常以定义于threads.h的便利宏thread_local来使用。

typedef指定符在C语言文法中通常列作存储类指定符。但它是用于声明类型名,而非指定存储的。

文件作用域中的const且非extern名称在C中拥有外部链接(如同所有文件作用域声明的默认设定),但在C++中拥有内部链接。

[编辑] 示例

[编辑] 参考

  • 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

[编辑] 参阅

存储类指定符C++文档