zoukankan      html  css  js  c++  java
  • C-static,auto,register,volatile

    static

    一:静态,意思就是呆在一个地方,不想动,大概就是编译期间就确定地址了。首先了解下C中的进程内存布局:

    1)正文段(.text)——CPU执行的机器指令部分;一个程序只有一个副本;只读,防止程序由于意外事故而修改自身指令;

    2)初始化数据段(.data)——在程序中所有赋了初值的全局变量,存放在这里。

    3)非初始化数据段(.bss)——在程序中没有初始化的全局变量;内核将此段初始化为0。

    4)栈(stack)——增长方向:自顶向下增长;自动变量以及每次函数调用时所需要保存的信息(返回地址;环境信息)。

    5)堆(heap)——动态存储区。是向高地址扩展的数据类型,是自下向上的扩展方式。

    High addr

    命令行参数和环境变量

    stack

    ...

    heap

    其他段

    .bss

    .data

    .text

    二:static在C语言里面有两个作用,第一个是修饰变量,第二个是修饰函数。
    1、static修饰变量
    按照作用范围的不同,变量分为局部变量和全局变量。如果用static修饰变量,不论这个变量是全局的还是局部的都是存储在静态数据区(根据此变量是否初始化分别存储在.data和.bss段)。下面分开来讲:
    如果用static修饰全局变量,我们称其为静态全局变量。主要目的:
    使得其作用域仅限于变量被定义的文件中(即从变量定义处到本文件结尾处),其它文件不论通过什么方式都不能访问。


    如果用static修饰局部变量,我们称其为静态局部变量。目的主要有两个:
    1)在某个函数体里面定义的静态局部变量,只能在本函数体被访问,即使同一个文件的其它函数也访问不了。
    2)静态局部变量总存储在静态数据区,所以即使这个函数运行结束,这个静态局部变量的值不会被销毁,函数下次使用时仍然要用到这个值。实际上它的生存周期和整个进程同步。

    2、static修饰函数
    在函数前加static,则此函数成为静态函数(内部函数),我们用static修饰函数的主要目的是:
    1)用来表示不能被其它文件访问的一个函数(和用static修饰全局变量的目的一样,都是不允许其它文件访问)
    2)程序员不用担心自己编写的函数与其他文件的函数同名。

    auto

    auto在没有明确声明为static时默认自动添加的,比如
    int a;//等价于auto int a;

    register

    这个关键字请求编译器尽可能的将变量存在CPU内部寄存器中,而不是通过内存寻址访问,以提高效率。注意是尽可能,不是绝对。
    register修饰符暗示编译程序相应的变量将被频繁地使用,如果可能的话,应将其保存在CPU的寄存器中,以加快其存储速度。
    但是使用register修饰符有几点限制。
      首先,register变量必须是能被CPU所接受的类型。这通常意味着register变量必须是一个单个的值,并且长度应该小于或者等于整型的长度。不过,有些机器的寄存器也能存放浮点数。
      其次,因为register变量可能不存放在内存中,所以不能用“&”来获取register变量的地址。
      由于寄存器的数量有限,而且某些寄存器只能接受特定类型的数据(如指针和浮点数),因此真正起作用的register修饰符的数目和类型都依赖于运行程序的机器,而任何多余的register修饰符都将被编译程序所忽略。
    在某些情况下,把变量保存在寄存器中反而会降低程序的运行速度。因为被占用的寄存器不能再用于其它目的;或者变量被使用的次数不够多,不足以装入和存储变量所带来的额外开销。
    早期的C编译程序不会把变量保存在寄存器中,除非你命令它这样做,这时register修饰符是C语言的一种很有价值的补充。然而,随着编译程序设计技术的进步,在决定那些变量应该被存到寄存器中时,现在的C编译环境能比程序员做出更好的决定。实际上,许多编译程序都会忽略register修饰符,因为尽管它完全合法,但它仅仅是暗示而不是命令。

    volatile

    表示用volatile定义的变量会在程序外被改变,每次都必须从内存中读取,而不能把他放在cache或寄存器中重复使用。
    volatile 影响编译器编译的结果,指出,volatile 变量是随时可能发生变化的,与volatile变量有关的运算,不要进行编译优化,以免出错。
    例如:

    volatile int i=10; 
    int j = i; 
    ... 
    int k = i; 

    volatile 告诉编译器i是随时可能发生变化的,每次使用它的时候必须从i的地址中读取,因而编译器生成的可执行码会重新从i的地址读取数据放在k中。
    而优化做法是,由于编译器发现两次从i读数据的代码之间的代码没有对i进行过操作,它会自动把上次读的数据放在k中。而不是重新从i里面读。这样一来,如果i是一个寄存器变量或者表示一个端口数据就容易出错,所以说volatile可以保证对特殊地址的稳定访问,不会出错。
    一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是用到volatile变量的几个例子:
      1) 并行设备的硬件寄存器(如:状态寄存器)
      2) 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
      3) 多线程应用中被几个任务共享的变量

    下面来几个小问题,看看你能回答上来不。
      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; 
    }
  • 相关阅读:
    WebFrom与MVC异同
    MVC解决WebFrom的缺点
    转载ORM--EF框架
    转载 HashSet用法 合交并差
    用户管理模块数据库设计
    外键的增删改查练习
    索引:如何让主键不自动创建聚集索引???
    SQL-类型转换函数
    SQL-union
    SQL字符串函数
  • 原文地址:https://www.cnblogs.com/seeken/p/5570154.html
Copyright © 2011-2022 走看看