关键字static、extern、volatile、详解及举例

一、预备知识

   1、 什么是局部变量?什么是全局变量?

          所谓局部变量,就是指在函数内部定义的变量的,只在该函数范围内有效。

            全局变量是指,在函数外部定义的变量为外部变量,即全局变量。它的有效范围是从定义变量的位置开始,到此源文件(如.c文件)结束的位置。这个变量在同一个工程中的其他源文件是没有起作用的。

 

  2、变量的存储方式

         从上面的分类中可以看出,从变量的作用域角度可以分为 局部变量全局变量。其实,从变量的存在的时长来看,可以分为静态存储方式动态存储方式

 那么,什么是静态存储方式?什么是动态存储方式呢?

   静态存储方式就是在程序运行的时候,由系统分配的,固定的存储空间的方式。比如,有的变量在程序运行的整个过程都是一直存在的,就是静态存储方式。

          动态存储方式就是在程序运行时候,根据需要进行,动态分配存储空的方式。比如,调用某个函数的时候,函数内的变量先是临时分配存储空间,然后调用结束后,再释放掉该部分的空间。函数的形参,函数中定义的局部变量,都是动态存储方式

 


 

二、关键字的含义到底是啥意思?

 1 、Static是什么意思?

       我们说过,局部变量在使用完之后,它的存储空间会被释放掉。如果我们不想将这些变量释放,而继续保留它的值,应该怎么办呢?这就应该使用static关键字进行声明。把这个局部变量指定为 “静态局部变量”。那么,下一次再次调用含有该变量的函数的时候,上一次调用的结果就会保留。因为它的存储空间一直没有变动过。

       举个栗子,

 1 #include<stdio.h>
 2 int main()
 3 {
 4     int add(int a);//声明一个加法函数
 5     int a = 2, i;
 6     for (i = 0; i < 3; i++)
 7     {
 8         printf("%d\n", add(a)); //输出add(a)的值
 9     }
10 }
11 
12 //add函数的定义
13 int add(int a)
14 {
15     int b = 0;
16     static c = 3;
17     b = b + 1; 
c = c + 1; 18 return (a + b + c); 19 }

    不急,不用慌,我们来简单分析这个程序。

         首先,主函数第一次调用了add函数,进入子函数,子函数中返回一个a+b+c即 2+1+4=7; 所以输出结果为7;

        然后第二次再次调用add函数,如果没有static 关键字声明变量c, 那么依然执行2+1+4=7;但是,加上static之后,c的值不是初始时候的3,而是上一次执行c=c+1,之后改变成了4。第二次调用时候,c的值一开始已经变成了4,执行c=c+1后,变为5! 这就是static的魅力!所以,第二次执行结果为2+1+5=8;

       同理,第三次调用add函数时候,c的值一开始已经变成了5,执行c=c+1后,变为6,输出结果为9;

 

故运行的结果为:

 

 

 

 

 注:虽然静态局部变量在函数调用结束后,依然存在,但是其他函数是不能引用它的。因为它是局部变量,只能被本身的函数使用。

 

 

        上面讲述的局部变量增加Static 的情行,我也见过全局变量的前面也加上static的声明,这又是什么意思呢?

 有时候,在程序设计中,希望外部变量只限制在本文件中,不想让其他文件引用。那么可以在外部变量的定义处增加一个static声明;

 file1.c

 static int M;  //在外部变量加上static 声明;
 int main()
 {
   .......
 
 }

 这种变量,叫做静态外部变量。

 

 

2、extern 是啥意思?

  前文提到,全局变量的作用范围,是指,从定义该变量的位置到该源文件结束的地方。但是呢,有的时候,我们想要扩大全局变量的范围,就需要使用extern关键字了。

     全局变量的作用范围已经这么光了,还需要扩大?有以下几种情况是需要扩大的。

    (1)在定义处之前的地方,是不能使用该外部变量的。可以用extern 声明一下;

     (2)将外部变量的作用范围拓展到其他文件

                一个C程序是由很多给c源文件组成的,如果别的源文件也想共用这个变量(跨文件使用),那么,也需要用extern 声明;

          举个栗子

在file1 文件中 写入下面内容:

 #include<stdio.h>
int A;          //定义一个全局变量
int main()
{
    int add(int);           //函数声明;
    int b = 3, c=4, d,n ;
    printf("enter the number A :\n");   
    scanf("%d,", &A);                //输入A的值
    d = A * b;
    printf("%d *%d=%d\n", A, b, d);  //输出 A*b 

    n = add(c);

    printf("%d +%d=%d\n", A, c, n);

}

在file2 文件中写入下面代码:

1 extern A;
2 int add(int n)  //定义add函数;
3 {
4     return (A + n);
5 }

  我们再来分析一下,首先main 函数中,计算了A*b的值,即A*3 ;假设A 输入5,那么第一个结果为 15;

       后来执行了add 函数,计算了A+c的值,即 A+4 ;假设A 输入5,那么第二个结果为 9;

       我们来看看是不是

    

 

 

 注意:假设某个程序包括了5个源文件,在一个文件中定义了全局变量A,其他4个文件要想引用A,必须在每一个文件中都加上“extern A;”声明;

 

3、volatile 是啥意思?

     先介绍一下cpu处理数据的过程,cpu从内存中读取到数据,然后放到cpu本身的寄存器中,然后经过计算使用,得到最终的结果值,再把结果写入的内存中。

volatile提醒编译器它后面所定义的变量随时都有可能改变,是不稳定的,易变化的。如果没有volatile关键字,则编译器可能优化读取和存储,可能暂时使用寄存器中的值,如果这个变量由别的程序更新了的话,将出现不一致的现象。

     因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从内存中读取数据。

    举个栗子,

    

static int flag;
 
void  add(void) 
{ flag
= 0; while (flag != 5); }

     上面这段代码,我们定义了flag 这个变量,设置它的值为0,然后while()进行无限循环,我们可以看到,如果只有这个程序,flag 的值不会变成5,永远都是0,那么编译器就会“擅作主张”地把它优化为如下:

void add_optimized(void) {
    flag = 0;
 
    while (true);
         
}

  但是,flag 可能指向一个随时能被计算机其他部分修改的地址,比如硬件的寄存器。那么,被优化的代码就永远检测不到这样的修改变化,为了阻止编译器像上面那样优化代码,需要使用volatile关键字:

static volatile int flag;

     这样修改以后,循环条件就不会被编译器优化掉,当值改变的时候系统将会检测到。

 

posted @ 2022-02-20 22:21  雨落城  阅读(818)  评论(0)    收藏  举报