zoukankan      html  css  js  c++  java
  • 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;

    }

  • 相关阅读:
    Linux内核网络协议栈优化总纲
    Java实现 蓝桥杯VIP 算法训练 连续正整数的和
    Java实现 蓝桥杯VIP 算法训练 连续正整数的和
    Java实现 蓝桥杯VIP 算法训练 寂寞的数
    Java实现 蓝桥杯VIP 算法训练 寂寞的数
    Java实现 蓝桥杯VIP 算法训练 学做菜
    Java实现 蓝桥杯VIP 算法训练 学做菜
    Java实现 蓝桥杯VIP 算法训练 判断字符位置
    Java实现 蓝桥杯VIP 算法训练 判断字符位置
    Java实现 蓝桥杯VIP 算法训练 链表数据求和操作
  • 原文地址:https://www.cnblogs.com/sdgwc/p/3227364.html
Copyright © 2011-2022 走看看