静态全局变量和静态易失性变量有什么区别?

发布于 2024-07-10 01:58:07 字数 237 浏览 8 评论 0原文

我在文件范围内使用了静态全局变量和静态易失性变量,

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

在优化过程中,全局变量和易失性变量都没有被优化。 因此,全局变量可以解决这个问题,而不是使用易失性变量。

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

使用静态易失性有什么具体原因吗?

任何示例程序都将是值得赞赏的。

提前致谢..

I have used a static global variable and a static volatile variable in file scope,

both are updated by an ISR and a main loop and main loop checks the value of the variable.

here during optimization neither the global variable nor the volatile variable are optimized. So instead of using a volatile variable a global variable solves the problem.

So is it good to use global variable instead of volatile?

Any specific reason to use static volatile??

Any example program would be appreciable.

Thanks in advance..

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

鸵鸟症 2024-07-17 01:58:07

“易失性”关键字建议编译器不要对涉及该变量的代码进行某些优化; 如果您只使用全局变量,则没有什么可以阻止编译器错误地优化您的代码。

示例:

#define MYPORT 0xDEADB33F

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

如果没有“易失性”,第一次写入可能会被优化掉。

The "volatile" keyword suggests the compiler not to do certain optimizations on code involving that variable; if you just use a global variable, nothing prevents the compiler to wrongly optimize your code.

Example:

#define MYPORT 0xDEADB33F

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

Without "volatile", the first write may be optimized out.

挖鼻大婶 2024-07-17 01:58:07

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

维基百科上的易失性变量

The volatile keyword tells the compiler to make sure that variable will never be cached. All accesses to it must be made in a consistent way as to have a consistent value between all threads. If the value of the variable is to be changed by another thread while you have a loop checking for change, you want the variable to be volatile as there is no guarantee that a regular variable value won't be cached at some point and the loop will just assume it stays the same.

Volatile variable on Wikipedia

狼性发作 2024-07-17 01:58:07

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

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

从长远来看,从一开始就使用适当的多线程结构会更安全,即使现在看起来没有它们也能正常工作。

当然,如果你的程序不是多线程的那就没关系。

They may not be in different in your current environment, but subtle changes could affect the behavior.

  • Different hardware (more processors, different memory architecture)
  • A new version of the compiler with better optimization.
  • Random variation in timing between threads. A problem may only occur one time in 10 million.
  • Different compiler optimization settings.

It is much safer in the long run to use proper multithreading constructs from the beginning, even if things seem to work for now without them.

Of course, if your program is not multi-threaded then it doesn't matter.

不再让梦枯萎 2024-07-17 01:58:07

我+1弗里奥尔的答案。 我想补充一些精确性,因为不同的答案似乎有很多混淆:C 的 易失性不是 Java 的易失性。

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

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

I +1 friol's answer. I would like to add some precisions as there seem to be a lot of confusions in different answers: C's volatile is not Java's volatile.

So first, compilers can do a lot of optimizations on based on the data flow of your program, volatile in C prevents that, it makes sure you really load/store to the location every time (instead of using registers of wiping it out e.g.). It is useful when you have a memory mapped IO port, as friol's pointed out.

Volatile in C has NOTHING to do with hardware caches or multithreading. It does not insert memory fences, and you have absolutely no garanty on the order of operations if two threads do accesses to it. Java's volatile keyword does exactly that though: inserting memory fences where needed.

机场等船 2024-07-17 01:58:07

易失性变量意味着分配给它的值不是恒定的,即如果一个函数包含易失性变量“a=10”并且该函数在该函数的每次调用中加1,那么它将始终返回更新的值。
<代码>{
易失性 int a=10;
一个++;
}
当上面的函数被一次又一次调用时,变量a将不会被重新初始化为10,它会一直显示更新的值,直到程序运行。
第一个输出= 10
然后 11
然后12
等等。

volatile variable means that the value assinged to it is not constant, i.e if a function containing a volatile variable "a=10" and the function is adding 1 in each call of that function then it will always return updated value.
{
volatile int a=10;
a++;
}

when the above function is called again and again then the variable a will not be re-initialised to 10, it will always show the updated value till the program runs.
1st output= 10
then 11
then 12
and so on.

淡墨 2024-07-17 01:58:07

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

因此,您可以将问题简化为全局变量与易失性变量。

现在讨论 volatile:

const 一样,volatile 是一个类型修饰符。

创建易失性关键字是为了防止编译器优化可能导致代码不正确,特别是在存在异步事件时。

声明为易失性的对象可能无法在某些优化中使用。

系统始终在使用易失性对象时读取其当前真实值,即使先前的指令要求来自同一对象的值。 此外,对象的值在赋值时立即写入。 这意味着没有将易失性变量缓存到 CPU 寄存器中。

博士。 Jobb 有一篇关于 volatility 的精彩文章

以下是 Dr. 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:

带有指针的 vola,与带有指针的 const 一样工作。

易失性 int * 类型的变量表示指针指向的变量是易失性的。

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

First let me mention that a static global variable, is the same as a global variable, except that you are limiting the variable to the scope of the file. I.e. you can't use this global variable in other files via the extern keyword.

So you can reduce your question to global variables vs volatile variables.

Now onto volatile:

Like const, volatile is a type modifier.

The volatile keyword was created to prevent compiler optimizations that may make code incorrect, specifically when there are asynchronous events.

Objects declared as volatile may not be used in certain optimizations.

The system always reads the current true value of a volatile object at the point it is used, even if a previous instruction asked for a value from the same object. Also, the value of the object is written immediately on assignment. That means there is no caching of a volatile variable into a CPU register.

Dr. Jobb's has a great article on volatile.

Here is an example from the Dr. Jobb's article:

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

If the compiler sees that Sleep() is an external call, it will assume that Sleep() cannot possibly change the variable flag_'s value. So the compiler may store the value of flag_ in a register. And in that case, it will never change. But if another thread calls wakeup, the first thread is still reading from the CPU's register. Wait() will never wake-up.

So why not just never cache variables into registers and avoid the problem completely?
It turns out that this optimization can really save you a lot of time overall. So C/C++ allows you to explicitly disable it via the volatile keyword.

The fact above that flag_ was a member variable, and not a global variable (nor static global) does not matter. The explanation after the example gives the correct reasoning even if you're dealing with global variables (and static global variables).

A common misconception is that declaring a variable volatile is sufficient to ensure thread safety. Operations on the variable are still not atomic, even though they are not "cached" in registers

volatile with pointers:

Volatile with pointers, works like const with pointers.

A variable of type volatile int * means that the variable that the pointer points to is volatile.

A variable of type int * volatile means that the pointer itself is volatile.

撞了怀 2024-07-17 01:58:07

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

全局

全局只是意味着所讨论的标识符是在文件范围内声明的。 有不同的作用域,称为函数(定义 goto 标签的地方)、文件(全局变量所在的地方)、块(普通局部变量所在的地方)和函数原型(函数参数所在的地方)。 这个概念的存在只是为了构建标识符的可见性。 它与优化没有任何关系。

静态

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

C99 草案中的 6.2.2/2

如果声明一个文件范围
对象或函数的标识符
包含存储类别说明符
静态的,标识符有内部
链接。

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

易失性

易失性是不同的东西:(6.7.3/6)

具有 volatile 限定的对象
类型可能会以未知的方式修改
实施或有其他
未知的副作用。 因此任何
引用此类对象的表达式
应严格按照
遵循抽象机的规则,
如 5.1.2.3 中所述。 此外,
在每个序列点最后的值
存储在对象中应符合
摘要规定的
机器,除非经过修改
提到的未知因素
以前。

该标准提供了一个很好的例子,其中易失性将是多余的(5.1.2.3/8):

一个实现可能会定义一个
之间一一对应
抽象和实际语义:at
每个序列点的值
实际物体会同意
那些由摘要指定的
语义。 关键字易失性
那么就显得多余了。

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

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

为了提供易于理解的概述,英特尔有一篇关于它的精彩文章 此处

我现在应该怎么做?

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

They are different things. I'm not an expert in volatile semantics. But i think it makes sense what is described here.

Global

Global just means the identifier in question is declared at file-scope. There are different scopes, called function (where goto-labels are defined in), file (where globals reside), block (where normal local variables reside), and function prototype (where function parameters reside). This concept just exist to structure the visibility of identifiers. It doesn't have anything to do with optimizations.

Static

static is a storage duration (we won't look at that here) and a way to give a name declared within file scope internal linkage. This can be done for functions or objects only required within one translation unit. A typical example might be a help function printing out the accepted parameters, and which is only called from the main function defined in the same .c file.

6.2.2/2 in a C99 draft:

If the declaration of a file scope
identifier for an object or a function
contains the storage class specifier
static, the identifier has internal
linkage.

Internal linkage means that the identifier is not visible outside the current translation unit (like the help function of above).

Volatile

Volatile is a different thing: (6.7.3/6)

An object that has volatile-qualified
type may be modified in ways unknown to
the implementation or have other
unknown side effects. Therefore any
expression referring to such an object
shall be evaluated strictly according
to the rules of the abstract machine,
as described in 5.1.2.3. Furthermore,
at every sequence point the value last
stored in the object shall agree with
that prescribed by the abstract
machine, except as modified by the
unknown factors mentioned
previously.

The Standard provides an excellent example for an example where volatile would be redundant (5.1.2.3/8):

An implementation might define a
one-to-one correspondence between
abstract and actual semantics: at
every sequence point, the values of
the actual objects would agree with
those specified by the abstract
semantics. The keyword volatile
would then be redundant.

Sequence points are points where the effect of side effects concerning the abstract machine are completed (i.e external conditions like memory cell values are not included). Between the right and the left of && and ||, after ; and returning from a function call are sequence points for example.

The abstract semantics is what the compiler can deduce from seeing only the sequence of code within a particular program. Effects of optimizations are irrelevant here. actual semantics include the effect of side effects done by writing to objects (for example, changing of memory cells). Qualifying an object as volatile means one always gets the value of an object straight from memory ("as modified by the unknown factors"). The Standard doesn't mention threads anywhere, and if you must rely on the order of changes, or on atomicity of operations, you should use platform dependent ways to ensure that.

For an easy to understand overview, intel has a great article about it here.

What should i do now?

Keep declaring your file-scope (global) data as volatile. Global data in itself does not mean the variables' value will equal to the value stored in memory. And static does only make your objects local to the current translation unit (the current .c files and all other files #include'ed by it).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文