静态全局变量和静态易失性变量有什么区别?
题
我在文件范围内使用了静态全局变量和静态易失性变量,
两者均由 ISR 和主循环更新,并且主循环检查变量的值。
在优化过程中,全局变量和易失性变量都没有被优化。因此,全局变量可以解决这个问题,而不是使用易失性变量。
那么使用全局变量而不是 volatile 好吗?
使用静态易失性有什么具体原因吗?
任何示例程序都将是值得赞赏的。
提前致谢..
解决方案
它们是不同的东西。我不是易变语义方面的专家。但我认为这里描述的内容是有道理的。
全球的
全局仅意味着所讨论的标识符是在文件范围内声明的。有不同的作用域,称为函数(定义 goto 标签的地方)、文件(全局变量所在的地方)、块(普通局部变量所在的地方)和函数原型(函数参数所在的地方)。这个概念的存在只是为了构建标识符的可见性。它与优化没有任何关系。
静止的
static
是一个存储持续时间(我们不会在这里看到它)和一种给出在文件范围内部链接中声明的名称的方法。这可以针对仅在一个翻译单元内所需的函数或对象来完成。一个典型的例子可能是 help
函数打印出接受的参数,并且仅从 main
定义在同一个函数中 .c
文件。
6.2.2/2 在 C99 草案中:
如果对对象或函数的范围识别范围的声明包含存储类规范静态,则识别器具有内部链接。
内部链接意味着标识符在当前翻译单元之外不可见(如 help
上面的函数)。
易挥发的
挥发性是另一回事:(6.7.3/6)
具有挥发性优先类型的对象可以以实现未知的方式进行修改或具有其他未知的副作用。因此,如5.1.2.3中所述,应严格根据抽象机器的规则对任何提到这种对象的表达式进行严格评估。此外,在每个序列上,对象中最后存储的值均与抽象机规定的值一致,除非由前面提到的未知因子进行了修改。
该标准提供了一个很好的例子,其中 volatile
将是多余的(5.1.2.3/8):
实现可以定义摘要和实际语义之间的一对一对应:在每个序列上,实际对象的值都会与抽象语义指定的值一致。关键词
volatile
那么就会是多余的。
序列点是有关副作用的影响的点 抽象机 已完成(即不包括存储单元值等外部条件)。的右侧和左侧之间 &&
和 ||
, , 后 ;
例如,从函数调用返回是序列点。
这 抽象语义 是编译器仅通过查看特定程序中的代码序列即可推断出的内容。优化的效果在这里无关紧要。 实际语义 包括写入对象所产生的副作用(例如,更改内存单元)。将对象限定为易失性意味着人们总是直接从内存中获取对象的值(“由未知因素修改”)。标准没有在任何地方提到线程,如果您必须依赖更改的顺序或操作的原子性,则应该使用依赖于平台的方法来确保这一点。
为了便于理解概述,英特尔有一篇很棒的文章 这里.
我现在应该怎么做?
继续将文件范围(全局)数据声明为易失性数据。全局数据本身并不意味着变量的值将等于存储在内存中的值。静态只会使您的对象本地于当前翻译单元(当前 .c
文件和所有其他由它 #include 的文件)。
其他提示
首先我要提到的是一个静态全局变量,是同一个全局变量,除非你是限制变量的文件的范围。即你无法通过extern
关键字用在其他文件中这个全局变量。
所以,你可以减少全局变量VS 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_
的值存储在寄存器中。而在这种情况下,它永远不会改变。但是,如果另一个线程调用唤醒,第一个线程仍然从CPU的寄存器读取。 Wait()
绝不会被唤醒。
那么,为什么不只是从来没有缓存变量到寄存器,并完全避免这个问题?
事实证明,这种优化真的可以节省您的整体大量的时间。因此,C / C ++允许你通过volatile
关键字明确禁用它。
这flag_
上述事实是一个成员变量,而不是一个全局变量(也没有静态全局)没有关系。之后的例子解释让即使你正在处理全局变量(和静态全局变量)正确的推理。
一个常见的误解是声明变量volatile
足以确保线程安全。在可变操作仍没有原子,尽管它们没有在寄存器“缓存”
<强>易失性与指针:强>
挥发性在指针上,就像使用指针常量。
型volatile int *
的变量表示变量,该指针指向是挥发性的。
型int * volatile
的变量意味着指示器本身是挥发性的。
在“挥发性”关键字表明编译器不这样做涉及该可变代码某些优化;如果你只是使用全局变量,没有什么可以阻止编译器错误地优化你的代码。
示例:
#define MYPORT 0xDEADB33F
volatile char *portptr = (char*)MYPORT;
*portptr = 'A';
*portptr = 'B';
没有“挥发性”,第一写可被优化了。
volatile关键字告诉编译器,以确保变量都不会被缓存。所有访问必须以一致的方式作出让所有的线程之间的一致的值。如果变量的值,而你有一个循环检查的变化被另一个线程改变了,你想要的变量挥发,因为没有保证一个普通变量的值不会在某一时刻,环路被缓存将只是假设它保持不变。
维基百科上挥发性变量
他们可能不会在不同的当前环境中,但微妙的变化可能影响行为。
- 不同的硬件(更多的处理器,不同记忆的建筑)
- 一个新版本的编译器有更好的优化。
- 随机变化,在时间线。一个问题可能仅仅出现一次在10万美元。
- 不同的编译器化设置。
它是很安全,长远来说,使用适当的多线程的结构,从一开始,甚至如果情况工作似乎现在没有他们。
当然,如果你的节目不是多线然后它并不重要。
我+1夫里奥尔的答案。我想补充一些精度为,似乎有很多不同的答案的混乱:C动荡的不是Java的挥发性
因此,首先,编译器可以做很多优化的基于程序的数据流量,挥发性用C防止,这使得确定你真的加载/存储的位置每一次(而不是使用擦拭的寄存器出EG)。当你有一个存储器映射的IO端口,如夫里奥尔的指出它是有用的。
用C挥发性无关与硬件缓存或多线程。它不插入内存栅栏,你有操作的顺序上绝对没有garanty如果两个线程做访问它。 Java的挥发性关键字正是这么做虽然:在需要的地方插入存储器围栏
易失性可变意味着assinged给它的值不是恒定的,即如果含有挥发性变量“a = 10”的函数,并且该函数在该函数的每个调用加1然后将始终返回更新后的值。
{
volatile int a=10;
a++;
}
当上述功能,一次又一次地叫那么变量将不会被重新初始化为10,它会始终显示更新后的值,直到程序运行。
第一输出= 10
然后11
然后12
等。