龙空技术网

「C语言学习笔记」之作用域、链接和存储期

InfinityCoder 18

前言:

今天大家对“c语言动态存储和静态存储”可能比较关心,兄弟们都想要知道一些“c语言动态存储和静态存储”的相关内容。那么小编同时在网上网罗了一些有关“c语言动态存储和静态存储””的相关资讯,希望你们能喜欢,小伙伴们快快来了解一下吧!

C语言能让程序员恰到好处地控制程序,这是它的优势之一。程序员通过C的内存管理系统指定变量的作用域和声明周期,实现对程序的控制。合理使用内存存储数据是设计程序的一个要点。

1. 存储类别1.1 作用域

作用域描述程序中可访问标识符的区域。一个C变量的作用域可以是块作用域、函数作用域、函数原型作用域或文件作用域。

块作用域:

初学C语言时接触到的变量大都具有块作用域,块是用一对花括号括起来的作用域。例如整个函数体是一个块,函数中的任意复合语句也是一个块。定义在块中的变量具有块作用域(block scope),块作用域变量的可见范围是从定义处到包含该定义的块的末尾。另外,虽然函数的形式参数声明在函数的左右括号之前,但是它们也具有块作用域,属于函数体这个块。所以简单来说,我们使用的局部变量(包括函数的形式参数)都具有块作用域。

下面通过程序进行说明:

void fun2(int cat)      //cat具有块作用域,即最外层花括号{    int dog = 9;        //dog具有块作用域,即最外层花括号    int i;                  for (i = 0; i < 3; i++)    {        int x = cat * i;        ...        return x;    }        {        int i = 6;        printf("inside i = %d\n", i);    }    printf("outer i = %d\n", i);    return dog;}

注意:上面程序有对变量i定义了两次,但是程序并不会报错,因为后面的i它的作用域只存在于包含它的花括号中,通过打印i的值我们可以看见后面一次的赋值操作对先前的i是不产生任何影响的。

另外,在以前的标准中具有块作用域的变量都必须声明在块的开头。C99标准放宽了这一限制,允许在块中的任意位置声明变量。因此,对于for的循环头,还可以这样写:

for (int i = 0; i < 3; i < ++){    int x = cat * i;}

为了适应这个新特性,C99把块的概念扩展到包括for循环、while循环、do while循环和if语句所控制的代码,即使这些代码没有用花括号括起来,也算是块的一部分。所以,上面循环中的变量i被视为for循环块的一部分,它的作用域仅限于for循环。一旦程序离开for循环,就不能再访问i。

函数作用域:

仅用于goto语句的标签。这意味着即使一个标签首次出现在函数的内层块中,它的作用域也延伸至整个函数。如果在两个块中使用相同的标签会很乱,标签的函数作用域防止了这样的事情发生。

函数原型作用域:

用于函数原型中的形参名:

int fun(int x, int y);

函数原型作用域的范围是从形参定义处到原型声明结束。这意味着,编译器在处理函数原型中的形参时只关心它的类型,而形参名(如果有的话)通常无关紧要。而且,即使有形参名,也不必与函数定义中的形参名相匹配。

文件作用域:

变量的定义在函数的外面,具有文件作用域。具有文件作用域的变量,从它的定义处到该定义所在的文件末尾均可见。

#include <stdio.h>int hero = 8;void fun();int main(){    ...}void fun(){    ...}

这里变量hero具有文件作用域,main()和fun()函数均可以使用它。由于这样的变量可用于多个函数,所以文件作用域变量也称为全局变量。

2. 链接

C变量有三种链接属性:外部链接、内部链接或无连接。具有块作用域、函数作用域或函数原型作用域的变量都是无连接变量。这意味着这些变量属于定义它们的块、函数或原型私有,这很好理解。具有文件作用域的变量可以是外部链接或内部链接。外部链接变量可以在多文件程序中使用,内部链接的变量只能在单个文件中使用:

#include <stdio.h>int cat = 6;            //文件作用域,外部链接static int dog = 9;     //文件作用域,内部链接int main(){    ...}

该文件和同一程序的其他文件都可以使用变量cat。而变量dog属于文件私有,该文件中任意函数都可以使用它。

3.存储期

作用域和链接描述了标识符的可见性。存储期描述了通过这些标识符访问的对象的生存期。C对象有四种生存期:静态存储期、线程存储期、自动存储期、动态分配存储期。

如果对象具有静态存储期,那么它在程序的执行期间一直存在。文件作用域变量具有静态存储期。注意,对于文件作用域变量,关键字static表明了其链接属性,而非存储期。以static声明的文件作用域变量具有内部链接。但是无论是内部链接还是外部链接,所有的文件作用域变量都具有静态存储期。

县城存储期用于并发程序设计,程序设计可被分为多个线程。具有线程存储期的对象,从被声明时到线程结束一直存在。以关键字_Thread_local声明一个对象时,每个线程都获得该变量的私有备份。

块作用域的变量通常都具有自动存储期。当程序进入定义这些变量的块时,为这些变量分配内存;当退出这个块时,释放刚才为变量分配的内存。这种做法相当于把自动变量占用的内存视为一个可重复使用的工作区或暂存区。例如,一个函数调用结束后,其变量占用的内存可用于存储下一个被调用的函数的变量。

变长数组稍有不同,它们的存储期从声明处到块的末尾,而不是从块的开始到块的末尾。

我们目前为止使用的局部变量都是自动类别。例如,在下面的代码中,变量num和i在每次调用funA()函数时被创建,在离开函数时被销毁:

void funA(int num){    int i;    for (i = 0; i < num; i++)    {        printf("I need more.\n");    }    return 0;}

然而块作用域变量也能具有静态存储期。为了创建这样的变量,要把变量声明在块中,且在声明前面加上关键字static:

void funB(int num){    int i;    static int cout = 0;    cout++;    ...    return 0;}

这里变量cout存储在静态内存中,它的程序被载入到程序结束期间都存在。但是它的作用域定义在funB()函数块中。只有在执行该函数时,程序才能使用cout访问它所指的对象(但是,该函数可以给其他函数提供该存储区的地址以便间接访问对象,例如通过指针形参或返回值)。

文章为自己学习笔记,如有错误之处还望指出,欢迎大家一起学习交流~

标签: #c语言动态存储和静态存储 #c语言infinity