一文讲解C语言存储类型

在 C 语言中,存储类型 storage class 是指存储变量的内存类型。存储类型决定了变量的创建、销毁和保存的生命周期,称为存储期。存储类型分为自动存储类型、外部存储类型、静态存储类型和寄存器存储类型四种类型。

一文讲解C语言存储类型

C 语言使用 autoexternstaticregister 四个关键字来标识变量所在的存储类型。

auto

使用关键字 auto 定义的变量属于自动存储类型,称为自动变量,具有自动存储期、块作用域、无链接,存储于堆栈中。

自动存储期指的是程序执行到声明自动变量的代码块时,自动变量才被创建,程序执行流离开代码块时,自动变量便自行销毁。该代码块数次执行时,自动变量也会跟着创建销毁。

int sum(int n) {
    auto int sum = 0;    // auto 可以省略
    while (n > 0) {
        sum += n;
        n--;
    }
    return sum;
}

代码块内部声明的变量在缺省情况下就是自动变量,因此可以省略 auto 关键字。

extern

使用关键字 extern 定义的变量属于外部存储类型的变量,称为外部变量,具有文件作用域、外部链接和静态存储期。

静态存储期指的是变量在程序运行时创建,在执行期间始终存在,并在程序结束时销毁。文件作用域变量具有静态存储期。

// extdef.c
#include "extdef.h"
#include <stdio.h>

extern int exvalues;  // 声明变量,但并未分配内存

void def_val() {
    printf("%dn", exvalues);
}

// main.c

int exvalues;  // 定义变量,分配内存

extern void def_val();  

int main() {
    exvalues = 16;
    def_val();
    return 0;
}

编写完代码后,编译源文件,如下所示。

gcc main.c extdef.c -o main

编译后,运行可执行文件即可。从上面可以看出,如果源文件中使用的外部变量定义在另一个源文件中,则必须用 extern 在该文件中声明变量。

static

使用关键字 static 定义的变量属于静态存储类型,称为静态变量,具有文件作用域、内部链接和静态存储期。

变量的缺省存储类型取决于它的声明位置。凡是在任何代码块之外声明的变量总是存储于静态内存中,也就是不属于堆栈的内存,这类变量称为静态 static 变量。

// main.c
static int sval;    // 不显式指定初始值,静态变量将初始化为 0

int main() {
    exvalues = 16;
    
    static int ival = 20;
    return 0;
}

如上所示,在代码块外声明的 sval 变量是静态变量,而代码块内部声明的 ival 变量被 static 修饰,使它的存储类型从自动变为静态。变为静态变量的 ival 会在整个程序执行过程中一直存在,而不仅仅在声明它的代码块的执行时存在。

修改变量的存储类型并不表示修改变量的作用域。如上所示的 ival 变量只能在代码块内部访问。函数的形参不能声明为静态,因为实参总是在堆栈中传递给函数,用于支持递归。

register

使用关键字 register 定义的变量属于寄存器存储类型,称为寄存器变量,具有块作用域、无链接和自动存储期。

使用 register 关键字用于自动变量的声明,将变量变量存储于 CPU 的寄存器中,而不是内存中。

int sum(int n) {
    register int sum = 0;
    while (n > 0) {
        sum += n;
        n--;
    }
    return sum;
}

如上所示,被 register 修饰的 sum 变量为寄存器变量,它比存储于内存中的变量访问起来效率更高。编译器会根据寄存器可用内存的数量衡量,可能只选取前几个存储于寄存器中,其余的按自动变量处理,或者编译器有优化寄存器的方法,由编译器选取哪些变量存储于寄存器。

机器并不提供寄存器变量的地址。

寄存器变量具有块作用域,创建和销毁也与自动变量相同,但需要做些额外工作。使用寄存器变量的函数返回之前,这些寄存器必须恢复先前存储的值,确保调用者的寄存器变量未被破坏。许多机器使用运行时堆栈来完成这个任务。当函数开始执行时,它把需要使用的所有寄存器的内部都保存到堆栈中,当函数返回时,这些值再复制回寄存器中。

链接属性

标识符的链接属性用于程序中各个源文件编译并链接成可执行文件后,处理不同文件中出现的同名标识符。标识符的作用域与它的链接属性有关。

链接属性一共有外部链接 external、内部链接 internal 和无链接 none 三种链接属性。

  • external:能被其它源文件访问的变量或函数, 不论声明多少次,位于几个源文件都表示同一个实体。缺省情况下,在代码块之外声明的变量的链接属性为 external
  • internal:同一个源文件内的所有声明都指向同一个实体,但位于不同源文件的多个声明则分属不同的实体。
  • none:该标识符的多个声明被当作单独的实体。
static int sval;
int n = 10;
int sum(int n) {
    int sum = 0;
    while (n > 0) {
        sum += n;
        n--;
    }
    return sum;
}

如上所示,缺省的 n 的链接属性为 externalsval 的链接属性为 internalsum 的链接属性为 none

一文讲解C语言存储类型

原文始发于微信公众号(海人为记):一文讲解C语言存储类型

© 版权声明
THE END
喜欢就支持一下吧
点赞5 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容