C语言存储类别(Storage Class)

C语言中,存储类别(Storage Class)是一个标识符(也就是说函数和变量)的重要属性。存储类别决定着一个函数或变量的作用域(Scope,即可见性)和生命周期(Life time)。C语言中,主要有四种存储类别,即auto、register、static和extern,下面将一一说明。

在说明之前,先讲清楚几个概念:

(1) 定义(Definition)和声明(Declaration)

声明是通知编译器变量的名字(name),类型(type),作用域(scope),链接性(linkage)和变量的声明周期(Duration or Lifetime);而变量的定义会导致编译器给该变量分配内存。需要注意的是只能在变量定义的时候对其进行初始化。因为,只有变量的定义才导致内存分配才需要初始化,变量声明是声明的一块已经分配好的内存空间,该空间在变量定义的时候已经被初始化过。

(2)作用域(scope)

作用域是一个编译时概念,主要有两种:文件(file)和块(block)。文件作用域是指从变量的声明到文件的末尾,该变量都是可以访问的;块作用域是指从变量被声明到改程序块的末尾是可以被访问的。

(3)链接性(linkage)

在C语言中,大体来说,有两种类型的对象(这里不是指面向对象编程中的对象,只是一般意义上的对象,也可以理解为元素)外部对象和内部对象(External和Internal,也就是上面的连接性)。所有在函数外声明的对象都是外部的,包括函数(函数也必须在函数外定义,因此,这很容易理解);所有在函数内部的对象都是内部的,包括函数的参数。从这种意义上来说,C语言程序是由外部对象的集合构成的。只有外部对象能够参与跨文件和代码库之间的通讯。

链接性是一个链接时概念,用来定义对象是否能被跨文件访问,或者只能在一个文件中访问,主要有三种:外部(External),内部(Internal)和No Linkage。函数内部的任何对象,包括参数,变量和其它都是no linkage,因此它们只能够从函数内部被访问(当然,如果在函数内部用extern作前缀声明了一个对象,这就是说,它不是no linkage的,这里先不讨论这种情况);具有外部链接性的对象必须在程序的最外层(也就是说不能够在函数或语句块内部),外部链接性也是在函数外声明的函数或其它对象的默认链接性,所有名字相同的具有外部链接性的实例都是引用的程序中的同一对象(也就是说内存中的同一区域);当希望函数或其它对象只能够在一个文件内被访问,而不能被文件外访问时,应该使这种对象具有内部链接性,相同名字的具有内部连接性的对象只在同一个文件内引用的是同一个对象,通过在对象的声明前加static关键字可以将外部对象的链接性声明为内部链接性(当然,也可以在局部变量前加static,意义和这里不一样)。

外部链接性是指该变量能够被多个文件访问;内部链接性是指该变量只能够在被声明的文件中被访问。

(4)声明周期(duration)

变量的声明周期是一个运行时概念,主要有两种Temporary和Process。Temporary的变量只在函数或代码块执行的时候存在,这类变量存储在栈(stack)中;Process变量在程序运行的整个过程中都会存在,这类变量存储在内存中的静态存储区。

1、auto

局部变量的缺省存储类别是auto,也就是说,下面两个变量的定义在存储类别层面对编译器来说是一样的。

{
  int life;
  auto int love;
}

存储类别为auto类型的标识符只能用在函数内部,比如做局部变量。其实,这是一种节省内存的方法,因为存储类别为auto的变量只在需要的时候才存在:当程序进入了它们所在的函数中时才会创建它们,而当退出这个函数时,它们也会随之被销毁。

2、register

register存储类别用来定义应该被存储在寄存器而非内存的变量。这样,一个存储类别为register的变量的最大不能超过寄存器的存储空间(通常是一个字长),并且register类型的变量不能用一元的&运算符来(也就是取地址操作符)操作。(很容易理解,因为register类型的变量是存储在寄存器,而非内存,所以不能用&来取它的内存地址。)。通常,register类型的变量通常用于经常被访问的变量,比如计数器。

{
  register int counter;
}

当然,也要知道,register说明符只是建议编译器将该变量存储到寄存器中,但是,在实际中,编译器并不一定会这样做(比如,可能没有足够数量的寄存器供编译器使用)。实际上,现在性能优越的编译器能够识别出经常使用的变量,并将其放在寄存器中,而无需程序员给出register声明。

推荐阅读

相关推荐