• volatile的作用是什么


    volatile的作用是让程序在编译时,编译器不对程序做优化。优化有时候是ok的,但是有时候是自作聪明会造成程序不对,如果你的一个变量是易变的,不希望编译器帮我们做优化。就在这个变量定义时加volatile;

    加不加有没有差别,取决于编译器,如果编译器做了优化则有差异,如果编译器本身没做优化,那就没有差别。

    1 c语言中变量的定义

    C语言变量的标定定义,由四部分组成,但通常我们省略了前两部分。

    存储类型 特征修饰 数据类型 变量名

    存储类型 决定变量的存储位置
    特征修饰 决定变量的特征属性
    数据类型 决定变量的存储空间及数据范围
    变量名字 决定变量的引用标识

    在这里插入图片描述

    1.1 存储类型(存储位置)

    什么是存储类?
    存储类定义 C 程序中变量/函数的范围(可见性)和生命周期;

    四种存储类型:auto,static,register,extern
    • 1

    一、auto 自动类型(栈)

    在函数体内,auto声明的变量是局部变量存放到栈空间中,当函数执行完毕,栈空间就会被系统自动释放。

    #include 
    int main() {
    	//auto
        {
            int mount;
            //平时我们定义变量,没有类型,其实默认都是auto变量。
            // 但是关键字"auto"可以被省略。
            // 这些变量在函数被调用时分配存储方式
            // 函数调用结束后这些存储空间就被释放了。
            auto int month;
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    二、register 寄存器(CPU内部的寄存器)

    register称为寄存器型

    register 存储类用于定义存储在寄存器中而不是 RAM 中的局部变量。

    主要目的是想将所说明的变量放入CPU的寄存器存储空间中,这样可以加快程序的运行速度。

    但CPU的寄存器数量也是有限的,当没有申请到寄存器来存储此变量时,该变量则自动转为auto类型!!!(声明为寄存器存储类型的变量,不能够取地址!)

    声明为寄存器存储类型的变量,不能够取地址! 不能对它应用一元的 ‘&’ 运算符(因为寄存器没有地址)。

    {
            //对于一些频繁使用的变量,程序在执行的过程中,每次用到这些变量的时候,都要从内存取出来,运算完了之后还要写到内存中去
            // 循环执行的次数越多,花费的时间就越多,
            // 为提高效率,C++允许将局部变量放在CPU的寄存器上,需要用到时直接从寄存器上取出参加运算,就不用再到内存中取;
            register int i,sum=0;
            for(i=0;i<10000;i++){
                sum+=i;
            }
            printf("%d", sum);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    三、static 静态类型 (静态区)

    (1)在函数体内说明的static 存储类型的变量也是一种局部变量,与auto最大不同点是:static存储类型的变量在内存中是以固定地址存放的,而不是以堆栈方式存放的;只要整个程序还在继续运行,静态变量就不会随着说明它的程序段的结束而消失,当下次再调用该函数,该存储类型的变量不再重新说明,而且还保留上次调用结束的数值。

    (2)当static修饰一个全局变量时,它的作用则是限定了此全局变量不能被外部文件所引用,限定了该全局变量的作用域。

    (3)当static修饰一个局部函数时,同样的作用也是限定了本代码段的作用域仅限于本文件,不得被外部文件引用!!!

    #include 
    
    //声明一个 fun1 方法
    void fun1(void);
    
    //定义一个全局变量
    static int count = 10;
    
            //定义的整形变量a的存储方式是静态存储的,静态局部变量是放在静态存储区内分配存储单元的,在整个程序运行期间都不释放,跟全局变量一样长期占用内存。
            // 但是静态变量和全局变量还是不一样的,比如说,静态变量只能在所定义的函数内引用,静态局部变量在函数调用结束后是仍然存在的,但不能被其他函数引用。
            //静态局部变量是在编译时赋初值的,即只赋初值一次,在程序运行时它已经有了初值
            // 以后每次调用函数时不再对其重新复制,而只是保留上次函数调用结束时的值。
            // 在定义静态局部变量时,如不赋初值,则编译时自动赋初值为0。
            //有时在程序设计中定义一个全局变量,只限于被所在源文件引用,而不想让其他文件引用,则可在定义全局变量前面加static。
      
    int main() {
       //调用方法 10 次
        while (count--) 
            fun1();
      return 0}
    
    //定义 fun1 方法
    void fun1() {
        //只初始化一次 再次 调用不会值不会覆盖
        static int a = 5;
        a++;
        printf("a = %d,count =  %d\n", a, count);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    四、extern 外部类型 (数据段DATA)

    extern 存储类用于提供一个全局变量的引用,全局变量对所有的程序文件都是可见的。当您使用 extern 时,对于无法初始化的变量,会把变量名指向一个之前定义过的存储位置。

    当您有多个文件且定义了一个可以在其他文件中使用的全局变量或函数时,可以在其他文件中使用 extern 来得到已定义的变量或函数的引用。可以这么理解,extern 是用来在另一个文件中声明一个全局变量或函数。

    //两个类中的方法要放在一起编译
    #include 
    
    //声明一个 count 变量
    int count ; //在一个.c文件定义时分配空间,当在另一个.c文件获取外部变量extern int count;此时告诉编译器,已经在另个文件分配空间,就不用再分配空间了
    //声明外部 方法 assignment
    extern void assignment();
    int main()
    {
        //给count 赋值
        count = 5;
        //调用方法
        assignment();
    }
    
    
    
    #include 
    
    //获取外部的count
    extern int count;//告诉编译器,我已经有空间,不用再申请空间
    //定义方法assignment
    void assignment(void)
    {
        //方法中直接输出 count
        printf("count = %d\n", count);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    1.2 特征修饰(特征属性)

    const、volatile

    1.3 数据类型(存储空间及数据范围)

    1.4 变量名字(引用标识)

    2 C语言中变量的访问

    1.读变量
    内存→寄存器(CPU),从内存读到寄存器

    ⒉写变量
    寄存器(CPU)-→内存

    在这里插入图片描述
    在这里插入图片描述

    3 volatile

    3.1 volatile 简介

    volatile是一个特征修饰符,是用来修饰易变的变量。
    易变是因为外在因素引起的,像多线程、中断等。。。

    表明某个变量的值可能在外部被改变,因此对这些变量的存取不能缓存到寄存器,每次使用时需要重新读取内存。

    编译器优化:编译器有一种技术叫做数据流分析,分析程序中的变量在哪里赋值、在哪里使用、在哪里失效,分析结果可以用于常量合并,常量传播等优化,进一步可以消除一些代码。但有时这些优化不是程序所需要的,这时可以用volatile修饰,禁止编译器优化。

    在这里插入图片描述

    3.2 volatile三种应用场合

    ①中断服务程序中修改的供其它程序检测的变量需要加volatile;
    ②多任务环境下各任务间共享的标志应该加volatile;
    ③存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能有不同意义;
    在这里插入图片描述
    (1)中断
    在这里插入图片描述(2)多线程
    在多线程时,多个线程都要访问同一个变量a,假如线程1在cpu1执行a,同时线程2又在cpu2执行a,那么当线程1再次执行a时,,a可能已经被改了,,每次执行都需要从内存重新读取a,而不能直接从寄存器读,使用上一次的值。
    在这里插入图片描述

    (3)硬件寄存器
    比如,AD转换,电压值可能实时变化的。

    3.3 volatile 举例说明**

    举例1:

    int MQ;
    MQ=0;
    MQ=1;
    MQ=2;
    MQ=3;
    
    • 1
    • 2
    • 3
    • 4
    • 5

    对外部硬件而言,上述四条语句分别表示不同的操作,会产生四种不同的动作,但是编译器却会对上述四条语句进行优化,认为只有MQ=3;(即忽略前三条语句,只产生一条机器代码)。
    如果键入volatile,则编译器会逐一地进行编译并产生相应的机器代码(产生四条代码)。

    举例2:

    for(int i=0; i<100000; i++);
    
    • 1

    这个语句用来测试空循环的速度的
    但是编译器肯定要把它优化掉,根本就不执行。
    改成下面:

    for(volatile int i=0; i<100000; i++);
    
    • 1

    就会执行。

    3.4 volatile 问题

    一个参数既可以是const还可以是volatile吗?
    可以的,例如只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。

    一个指针可以是volatile 吗?
    可以,当一个中服务子程序修该一个指向一个buffer的指针时。

    volatile 和extern可以同时使用吗?
    可以,双重属性叠加,不冲突。

    注意:频繁的使用volatile很可能会增加代码的尺寸和降低性能,因此要合理的使用volatile。

    volatile的作用是让程序在编译时,编译器不对程序做优化。优化有时候是ok的,但是有时候是自作聪明会造成程序不对,如果你的一个变量是易变的,不希望编译器帮我们做优化。就在这个变量定义时加volatile;

    加不加有没有差别,取决于编译器,如果编译器做了优化则有差异,如果编译器本身没做优化,那就没有差别。

  • 相关阅读:
    postman-接口自动化
    【HTML】
    springboot +shiro 缓存用户退出bug
    对Python3.8配置OpenCV4.5.5中
    nnDetection复现Luna16 附模型
    IOS 16 RC升级 IOS 16 步骤
    el-date-picker 日期时间选择器 限时时间范围 精确到时分秒
    网络学习(12)|性能优化与调试:HTTP性能优化与分析
    申请宣告专利权无效的主体有哪些 ?
    Ansible --- playbook 剧本
  • 原文地址:https://blog.csdn.net/m0_51233386/article/details/126677788