• 基础C语言知识串串香12☞存储类&作用域&生命周期&链接属性


    6217760-7a1d63bdb0bc5b7a.jpg

    七、存储类&作用域&生命周期&链接属性

    7.1、概念词:存储类(栈、堆、数据区、.bss段、.text段)
    作用域(代码块作用范围,也就是变量作用的范围)
    生命周期(变量的诞生和死亡)
    链接属性(外链接属性、内链接属性、无连接属性)

    7.2、Linux下的内存映射(分配情况、组织情况):见图内存映射。其中有关进程的空间,如进程控制块、页表等都是在内核里面的。文件区是映射外部文件的,如打开记事本,那么这个文件临时存放在文件区域。(见引用资料)

    问题:虚拟地址技术? 解决:后期在Linux应用/网络编程会讲。

    OS下和裸机下C程序加载执行的差异? 解决:在arm裸机第十六部分有介绍。

    7.3、存储类关键字:

    <1> auto 自动的(一个用法:修饰局部变量,在定义变量时可以省略) 【外链接:与第二个c文件链接】【内链接:只与本c文件链接】【无连接:就是无链接】

    <2> static 静态的(有两个用法,第一个是修饰局部变量,意思是当作全局变量,存放在数据区,作用域只是定义的那个函数范围,生命周期和整个程序一样,属于无连接

    第二个是修改全局变量/函数,意思是这个全局变量/函数只在当前c文件有效,其他c文件是不能使用它的,属于内链接,普通全局变量属于外连接)
    <3>register 寄存器(一个用法,修饰变量,作用是让编译器把这个变量放在寄存器中,当这个变量频繁的被使用时,用这个方法可以提高效率,但有时候不一定就放在寄存器,因为寄存器是有限的,没有剩余的寄存器了)

    <4>extern (一个用法,修饰全局变量,表示该文件要使用的这个变量,在另外一个c文件中已经定义了,其一个声明的作用,不能初始化)

    <5>volatile (一个用法,修饰变量,表示对这个变量的语句不要去优化)

    (1) volatile的字面意思:可变的、易变的。C语言中volatile用来修饰一个变量,表示这个变量可以被编译器之外的东西改变。编译器之内的意思是变量的值的改变是代码的作用,编译器之外的改变就是这个改变不是代码造成的,或者不是当前代码造成的,编译器在编译当前代码时无法预知。譬如在中断处理程序isr中更改了这个变量的值,譬如多线程中在别的线程更改了这个变量的值,譬如硬件自动更改了这个变量的值(一般这个变量是存在寄存器,或许当其他进程要用到这个寄存器时,就把这个寄存器的变量给改变了,同时也就改变了这个变量)

    (2) 以上说的三种情况(中断isr中引用的变量,多线程中共用的变量,硬件会更改的变量)都是编译器在编译时无法预知的更改,此时应用使用volatile告诉编译器这个变量属于这种(可变的、易变的)情况。编译器在遇到volatile修饰的变量时就不会对改变量的访问进行优化,就不会出现错误。

    (3) 编译器的优化在一般情况下非常好,可以帮助提升程序效率。但是在特殊情况(volatile)下,变量会被编译器想象之外的力量所改变,此时如果编译器没有意识到而去优化则就会造成优化错误,优化错误就会带来执行时错误。

    而且这种错误很难被发现。

    (4) volatile是程序员意识到需要volatile然后在定义变量时加上volatile,如果你遇到了应该加volatile的情况而没有加程序可能会被错误的优化。如果在不应该加volatile而加了的情况程序不会出错只是会降低效率。

    所以我们对于volatile的态度应该是:正确区分,该加的时候加不该加的时候不加,如果不能确定该不该加为了保险起见就加上。

    举例子1: int a=3 ,b,c;
    b=a;
    c=b;

    那么编译器会优化成 c=b=a=3; 如果此时遇到上述三种情况,突然改变了a的值,那么,对于没有优化前是对的,但是对于优化后,那么c仍然是3,就会出错。

    所以当我们程序员知道这个变量会发生改变时,就应该加 volatile,提示编译器不要帮我们做优化。

    举列子2:

    int square(volatile int *ptr)
       {
       return *ptr * *ptr;
       }

    这段代码的有个恶作剧。这段代码的目的是用来返指针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;
       }

    <6> restrict (1)c99中才支持的,所以很多延续c89的编译器是不支持restrict关键字,gcc支持的。

    (2)restrict 作用是让编译器对这个变量做一些优化,让它提高效率。下面的网站有列子。

    (3)restrict只用来修饰指针,不能修饰普通变量,它表示只能该指针才能修改它的内容。如用memcpy时,两个内存存在重叠的现象。

    (4)https://blog.chinaunix.net/uid-22197900-id-359209.html (这个网站里面有详细的例子)

    (5)memcpy和memmove的区别 void *memcpy( void * restrict dest ,const void * restrict src,sizi_t n);这样它可以优化成memmove原理的方式(当存在重叠时,先复制到一段内存空间,然后再把它复制到目的空间)

    7.4、作用域:

    (1)全局变量起名字一般是 g_a;

    (2)名字加前缀表示

    7.5、总结:<1>局部变量地址由运行时在栈上分配得到,多次执行时地址不一定相同,函数不能返回该类变量的地址(指针)作为返回值。

    <2>为什么要分为数据段和.bbs段?因为当加载到内存重定位时,如果这些数据(包括0)一个一个的复制,会降低效率,为0的变量,直接清内存就可以了,这样提高效率。

    <3>在头文件声明全局变量时, extern int a; 声明函数就是 void int max(int a, int b);

    <4>写程序尽量避免使用全局变量,尤其是非static类型的全局变量。能确定不会被其他文件引用的全局变量一定要static修饰。(因为全局变量占内存的时间是最长的,要看你的变量是不是需要这么长的时间,这样可以节约内存空)


    往期文章列表:****往期热文:
    基础C语言知识串串香(1)

    基础C语言知识串串香(2)

    基础C语言知识串串香(3)

    基础C语言知识串串香(4)

    基础C语言知识串串香(5)

    基础C语言知识串串香(6)

    基础C语言知识串串香(7)

    基础C语言知识串串香(8)

    基础C语言知识串串香(9)

    基础C语言知识串串香(10)

    基础C语言知识串串香(11)


    ===========我是华丽的分割线===========


    更多知识:
    点击关注专题:嵌入式Linux&ARM

    或浏览器打开:https://www.jianshu.com/c/42d33cadb1c1

    或扫描二维码:

    6217760-e6bba06e005d8fe7.jpg

  • 相关阅读:
    Appium脚本(2):元素检测
    查看appPackage和appActivity的多种方法
    让织梦内容页arclist标签的当前文章标题加亮显示
    dedecms wap 上一篇 下一篇 链接出错
    织梦开启二级域名(多站点)内容页图片无法显示的解决方法
    多级分类标签{dede:channelartlist}实现当前栏目颜色高亮显示
    织梦channelartlist标签当前栏目高亮
    dedecms模板中 if else怎么写
    dedecms调用子栏目及文章列表
    Dedecms判断当前栏目下是否有子栏目
  • 原文地址:https://www.cnblogs.com/leon1124/p/14039748.html
Copyright © 2020-2023  润新知