12-3. 存储类别

  • 学习人数 30K+
  • 适合所有人群学习
avatar
林耿亮

你好编程主讲老师

标识符作用域那一节中,我们讨论了标识符的作用域。在标识符的作用域内,标识符指代声明的数据对象。作用域外,该标识符没有意义。

#include <stdio.h>

void func()
{
    int n = 100;
}

int main()
{
    func();
    printf("%d\n", n);
    return 0;
}

例如,上面的代码中,标识符n声明在函数func中,它的作用域从声明int n = 100开始,直到func函数结束。因此,函数main中,标识符n不代表任何数据对象。若对这段代码进行编译。编译器将报错,错误信息为:未定义标识符n

那我们换一种思路,在函数main中,既然不能使用标识符来访问函数func中的整型变量n。那么,我们获取到整型变量n的指针,通过指针来访问它,是否可以呢?

1. 自动变量

#include <stdio.h>

int *func()
{
    int n = 100;
    return &n;
}

int main()
{
    int *pN = func();
    printf("%d\n", *pN);
    return 0;
}

在函数func中,我们获取整型变量n的指针,将该指针返回到函数main。在函数main中,使用指针来访问整型变量n

对代码进行编译,我们发现代码出现了一个警告。

返回局部变量的地址

我们再运行这段代码试试看。

使用野指针

运行结果好像是正常的。但是,这样做是错误的

默认情况下,声明在代码块内的任何变量都属于自动存储类别的变量

自动变量在程序运行到该变量声明所在的代码块时创建,离开该代码块时销毁。这一段变量存在的周期,被称作变量的生命周期或简称生命期

离开代码块后,变量n将被销毁。原属于变量n的空间不再保留。

事实上,为了指明一个变量属于自动存储类别,C语言中分配了关键词auto。你可以在变量声明前加关键词auto,声明该变量是一个自动变量。

#include <stdio.h>

int *func()
{
    auto int n = 100;   //  可以省略关键词auto
    return &n;
}

int main()
{
    auto int *pN = func();  //  可以省略关键词auto
    printf("%d\n", *pN);
    return 0;
}

但是,由于默认情况下,代码块内声明的变量均为自动变量,没必要将auto关键词写出来。因此,在C语言代码中,几乎不会出现关键词auto

接下来,让我们从作用域与生命期的角度分析一下变量n:

作用域与生命期

变量n的生命期:从包含变量n声明的代码块开始,直到包含声明的代码块结束为止。 变量n的作用域:从变量n的声明开始,直到包含声明的代码块结束为止。

请注意区分作用域与生命期的区别:

作用域

作用域为标识符对数据对象指代关系存在的区域,它是一种关联关系。

生命期

生命期是数据对象从创建到销毁之间,数据对象存在的周期。

在前面的代码中,虽然函数func返回了指向变量n的指针。但是,此时变量n的生命期已结束。该指针指向了一个已失效的空间。这种指针被称作野指针。访问野指针有可能看起来正常,有时候可能会崩溃,有时候该空间被其他地方重新利用而导致数据错乱。

由于自动变量拥有块内作用域及生命期,所以也被称作局部变量