extern、static、auto、register 定义变量的不同用法

首先得说明什么叫“编译单元”。每个 .c 文件会被编译为一个 .o 文件,这个就是一个编译单元。最后所有的编译单元被链接起来,就是一个库或一个程序。 

一个变量/函数,只要是在全局声明的,链接之后都隐含地在所有编译单元中可见。但你的声明可能仅出现在一个 .c 文件中,这就暗示你不想把这个名字暴露给其它编译单元,这种情况下就得用 static 关键字,表示这个名字具有“内部链接”,只对当前编译单元有效。但还有一种可能,你确实想暴露这个名字,但其它的编译单元希望知道这个名字被声明为什么类型,所以你需要在别的编译单元中用 extern 关键字描述这个声明,表示它具有“外部链接”,是在别的编译单元中定义的。不过通常的办法是在头文件中写出这个声明,让需要这个声明的文件包含它。 

那么函数中的 static 变量是怎么回事呢?前面已经说过,static 代表“内部链接”,就是说这个变量是定义在编译单元上的,程序开始执行前就已经存在,所以会有“函数退出后仍保持值”的效果;与此相对,函数中定义的自动变量是定义在栈上的,函数执行结束函数调用的活动记录就被清除。 

总结下来就是,extern/static 指出一个名字具有外部链接还是内部链接;名字的作用域只和名字被定义的位置有关。 

C中的auto、static、register和extern的区别

C语言中的每一个变量和函数有两个属性:数据类型和数据的存储类别。数据类型(整形、字符型等),存储类别是指数据在内存中存储的方法,存储方法有两大类:静态存储类和动态存储类。具体包括四种:自动的(auto),静态的(static),寄存器的(register)和外部的(extern)。

auto变量:函数中的局部变量,如不专门声明static,一般都是动态地分配存储空间。自动变量:在调用该函数时系统会给他们分配存储空间,一旦函数调用结束这些存储空间就会自动释放。关键字“auto”可以省略,不写则隐含确定为“自动存储类别”,属于动态存储方式。

static声明变量:用static声明的静态局部变量,在函数调用结束后不消失,反而保留当前的数据,在下一次该函数调用时,该变量现有的值就是上一次函数调用结束时的值。

一般用static声明一个变量的作用有二:(1)对局部变量用static声明,则为该变量分配的空间在整个程序执行期间始终存在。(2)对全部变量用static声明,则该变量的作用域只限于本文件模块,即被声明的文件中。

eg:f(int a)

       {

           auto b=0;               //将b定义为auto类型。

           static c=3;              //将c定义为static类型。

           b=b+1,c=c+1;

           return(a+b+c);

        }

      main()

       {

        int a=2,i;

        for(i=0;i<3;i++)

         printf("%d",f(a));

       }

在第一次调用f函数时b=0,c=3,第一次调用结束后b=1,c=4,a+b+c=7;执行完之后由于c是静态局部变量,在函数调用结束后,它并不释放,所以保留c=4。而b还是0。所以程序输出7,8,9。

static还可以声明函数,eg:static int fun(int a, int b)称fun为内部函数,或者静态函数。内部函数的使用只限于所在文件,而且不同文件中的同名内部函数互不干扰。

register变量:一般变量的值都是存储在内存中,(当程序需要用到哪一个变量的值,由控制器发出指令将内存中该变量的值送到运算器,完了如果需要存数,再从运算器将数据送到内存中存放。)所以就引出一个问题,如果我们进行一段频繁的运算,则存储变量的值肯定要花费不少时间,所以C语言允许将局部变量的值存放在寄存器中,这样需要时就直接搬用,不必再进行过内存。提高运算速度。

extern声明外部变量:外部变量(即全局变量)是在函数的外部定义的。作用域为从变量的定义处开始,到本程序文件的结尾。可以在一个文件内声明外部变量,如:

     main()

     {

       extern A,B;

       printf("%d",max(A,B));

     } int A=13,B=-8;

也可以在多文件的程序中声明外部变量。

extern还可声明函数,eg:extern int fun(int a, int b);声明的外部函数可供其他文件调用,在C中,定义函数时省略extern,则隐含为外部函数

另附加一个两个关键字const和volitate  

别人问起,不能简单说const表示常数,这样会让别人觉得很外行。或许可以说是只读,其实也不完全正确。务必要弄清楚一下几个定义的含义:

const int a;       //a是一个常整型数

int const a;      //a是一个整型常数

const int *a;    //a是一个指向常整型数的指针,从这里可以看出整型数不可以修改,但指针可以。

int * const a;   //a是一个指向整型数的常指针,整型数可以修改,指针不能修改。

int const * a const;   //a是一个指向常整型数的常指针。

如果能正确回答这些问题,那么他就给我留下了一个好印象。顺带提一句,也许你可能会问,即使不用关键字 const,也还是能很容易写出功能正确的程序,那么我为什么还要如此看重关键字const呢?我也如下的几下理由:

1). 关键字const的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。如果你曾花很多时间清理其它人留下的垃圾,你就会很快学会感谢这点多余的信息。(当然,懂得用const的程序员很少会留下的垃圾让别人来清理的。)

2). 通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。

3). 合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少bug的出现。

关键字volatile有什么含意 并给出三个不同的例子。

一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:

1). 并行设备的硬件寄存器(如:状态寄存器)

2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)

3). 多线程应用中被几个任务共享的变量

回答不出这个问题的人是不会被雇佣的。我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。嵌入式系统程序员经常同硬件、中断、RTOS等等打交道,所用这些都要求volatile变量。不懂得volatile内容将会带来灾难。

假设被面试者正确地回答了这是问题(嗯,怀疑这否会是这样),我将稍微深究一下,看一下这家伙是不是直正懂得volatile完全的重要性。

1). 一个参数既可以是const还可以是volatile吗?解释为什么。

2). 一个指针可以是volatile 吗?解释为什么。

3). 下面的函数有什么错误:

int square(volatile int *ptr)

{

return *ptr * *ptr;

}

下面是答案:

1). 是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。

2). 是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。

3). 这段代码的有个恶作剧。这段代码的目的是用来返指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码:

int square(volatile int *ptr)

{

int a,b;

a = *ptr;

b = *ptr;

return a * b;

}

由于*ptr的值可能被意想不到地该变,因此a和b可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下:

long square(volatile int *ptr)

{

int a;

a = *ptr;

return a * a;

}

原文地址:https://www.cnblogs.com/sdgwc/p/3227364.html