当前位置:  开发笔记 > 编程语言 > 正文

静态全局变量和静态volatile变量之间有什么区别?

如何解决《静态全局变量和静态volatile变量之间有什么区别?》经验,为你挑选了3个好方法。

我在文件范围中使用了静态全局变量和静态volatile变量,

两者都由ISR和主循环更新,主循环检查变量的值.

在优化期间,全局变量和volatile变量都不会被优化.因此,全局变量不是使用volatile变量来解决问题.

那么使用全局变量而不是volatile是否合适呢?

使用静态volatile的任何具体原因??

任何示例程序都是可观的.

提前致谢..



1> Brian R. Bon..:

首先让我提一下静态全局变量与全局变量相同,只是您将变量限制在文件的范围内.也就是说,你不能通过extern关键字在其他文件中使用这个全局变量.

因此,您可以将问题减少到全局变量和volatile变量.

现在变为volatile:

比如const,volatile是一个类型修饰符.

volatile关键字是为了防止编译器优化,可以使代码不正确,特别是当有异步事件.

声明为的对象volatile可能无法在某些优化中使用.

系统始终在使用它的位置读取易失性对象的当前真值,即使前一条指令要求来自同一对象的值也是如此.此外,在分配时立即写入对象的值.这意味着没有将易失性变量缓存到CPU寄存器中.

Jobb博士有一篇关于不稳定的文章.

以下是Jobb博士文章的一个例子:

class Gadget
{
public:
    void Wait()
    {
        while (!flag_)
        {
            Sleep(1000); // sleeps for 1000 milliseconds
        }
    }
    void Wakeup()
    {
        flag_ = true;
    }
    ...
private:
    bool flag_;
};

如果编译器看到这Sleep()是一个外部调用,它将假定Sleep()不可能更改变量flag_的值.因此编译器可以将值存储flag_在寄存器中.在这种情况下,它永远不会改变.但是如果另一个线程调用wakeup,第一个线程仍在从CPU的寄存器中读取.Wait()永远不会醒来.

那么为什么不直接将变量缓存到寄存器中并完全避免这个问题呢?事实证明,这种优化可以真正为您节省大量时间.因此,C/C++允许您通过volatile关键字明确禁用它.

上面的事实flag_是成员变量,而不是全局变量(也不是静态全局变量)并不重要.即使您处理全局变量(和静态全局变量),示例后面的解释也会给出正确的推理.

一个常见的误解是声明变量volatile足以确保线程安全.变量的操作仍然不是原子的,即使它们没有在寄存器中"缓存"

指针易失性:

使用指针进行易失性,像const一样使用指针.

类型变量volatile int *意味着指针指向的变量是易失性的.

类型变量int * volatile意味着指针本身是易失性的.


很好的答案.我唯一要添加的东西(因为这是一个常见的误解)是声明变量volatile不足以确保线程安全.变量的操作仍然不是原子的,即使它们没有在寄存器中"缓存"

2> Johannes Sch..:

他们是不同的东西.我不是volatile语义的专家.但我认为这里描述的是有道理的.

全球

全局只是意味着有问题的标识符在文件范围内声明.有不同的范围,称为函数(其中定义了goto-labels),文件(其中包含全局变量),块(正常局部变量驻留)和函数原型(函数参数驻留).这个概念只是用于构建标识符的可见性.它与优化没有任何关系.

静态的

static是一个存储持续时间(我们不会在这里查看)和一种在文件范围内部链接中声明名称的方法.这可以针对仅在一个翻译单元内所需的功能或对象来完成.典型示例可能是help打印出接受的参数的函数,并且仅从main同一.c文件中定义的函数调用.

C99草案中的6.2.2/2:

如果对象或函数的文件范围标识符声明包含特定于静态的存储类,则标识符具有内部链接.

内部链接意味着标识符在当前翻译单元外部不可见(如help上面的功能).

挥发物

易失性是另一回事:(6.7.3/6)

具有挥发性限定类型的对象可以以实现未知的方式修改或具有其他未知的副作用.因此,任何涉及这种对象的表达都应严格按照抽象机的规则进行评估,如5.1.2.3所述.此外,在每个序列点,最后存储在对象中的值应与抽象机器规定的值一致,除非由前面提到的未知因素修改.

标准为volatile冗余(5.1.2.3/8)的示例提供了一个很好的示例:

实现可以定义抽象语义和实际语义之间的一对一对应关系:在每个序列点,实际对象的值将与抽象语义指定的值一致.关键字volatile 将是多余的.

序列点是完成关于抽象机器的副作用的影响的点(即,不包括诸如存储器单元值的外部条件).右侧和左侧之间&&||之后,;并从函数调用返回的例如序列点.

抽象的语义是什么,编译器可以从特定的程序中看到的代码只序列推断.优化的效果在这里无关紧要.实际语义包括通过写入对象(例如,更改存储器单元)完成的副作用的影响.将对象限定为volatile意味着总是直接从内存中获取对象的值("由未知因素修改").标准没有在任何地方提到线程,如果您必须依赖更改的顺序或操作的原子性,您应该使用平台相关的方法来确保.

为了便于理解,intel 在这里有一篇很棒的文章.

我现在应该怎么做?

继续将文件范围(全局)数据声明为volatile.全局数据本身并不意味着变量的值将等于存储在内存中的值.静态只会使你的对象在当前翻译单元本地(当前.c文件和所有其他文件#include').



3> Gabriele D'A..:

"volatile"关键字表明编译器不对涉及该变量的代码进行某些优化; 如果您只使用全局变量,则不会阻止编译器错误地优化您的代码.

例:

#define MYPORT 0xDEADB33F

volatile char *portptr = (char*)MYPORT;
*portptr = 'A';
*portptr = 'B';

没有"易失性",可以优化第一次写入.

推荐阅读
jerry613
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有