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

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

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

在优化过程中,全局变量和volatile变量都不会被优化。 因此,全局变量不是使用volatile变量,而是解决问题。

那么使用全局变量而不是volatile是好的吗?

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

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

提前致谢..

它们是不同的东西。 我不是易失性语义方面的专家。 但我认为这里描述的是有道理的。

全球

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

静态的

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

C99草案中的6.2.2 / 2

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

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

挥发物

挥发性是另一回事:( 6.7.3 / 6

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

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

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

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

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

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

我现在应该怎么做?

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

首先让我提一下静态全局变量与全局变量相同,只是您将变量限制在文件范围内。 也就是说,你不能通过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()永远不会叫醒。

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

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

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

指针易失性:

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

volatile int *类型的变量意味着指针指向的变量是volatile。

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

“volatile”关键字表示编译器不对涉及该变量的代码进行某些优化; 如果你只使用一个全局变量,没有什么能阻止编译器错误地优化你的代码。

例:

 #define MYPORT 0xDEADB33F volatile char *portptr = (char*)MYPORT; *portptr = 'A'; *portptr = 'B'; 

没有“易失性”,可以优化第一次写入。

volatile关键字告诉编译器确保永远不会缓存该变量。 必须以一致的方式对所有访问进行访问,以便在所有线程之间具有一致的值。 如果在循环检查更改时要由另一个线程更改变量的值,则希望变量是易变的,因为不能保证在某个点和循环中不会缓存常规变量值我会假设它保持不变。

维基百科上的易变变量

它们在您当前的环境中可能不同,但细微的变化可能会影响行为。

  • 不同硬件(更多处理器,不同内存架构)
  • 新版本的编译器具有更好的优化。
  • 线程之间的时间随机变化。 问题可能只发生在1000万次中。
  • 不同的编译器优化设置

从长远来看,从一开始就使用适当的multithreading结构会更安全,即使现在看起来没有它们的东西似乎也可以工作。

当然,如果您的程序不是multithreading的,那么无关紧要。

我+1骗子的回答。 我想补充一些精度,因为在不同的答案中似乎存在很多混淆:C的volatile不是Java的易变性。

首先,编译器可以根据程序的数据流进行大量优化,C中的volatile可以防止这种情况,它确保每次都真正加载/存储到该位置(而不是使用擦除寄存器,例如) 。 当你有一个内存映射的IO端口时,它非常有用,正如friol所指出的那样。

C中的易失性与硬件缓存或multithreading无关。 它不会插入内存防护,如果两个线程访问它,您绝对不会对操作顺序保持警惕。 Java的volatile关键字就是这样做的:在需要的地方插入内存栅栏。

volatile变量意味着它的值不是常量,即如果一个函数包含一个volatile变量“a = 10”并且该函数在该函数的每次调用中都加1,那么它将始终返回更新的值。 { volatile int a=10; a++; } 当一次又一次调用上面的函数时,变量a将不会重新初始化为10,它将始终显示更新的值,直到程序运行。 第一输出= 10然后是11然后是12,依此类推。