为什么摆脱挥发物是危险的?

发布于 2024-09-12 21:51:45 字数 566 浏览 6 评论 0原文

在 C++ 中,易失性 的处理方式与 const 相同:将指向易失性数据的指针传递给不需要 易失性 修饰符的函数触发编译错误。

int foo(int* bar) { /* snip */ }

int main()
{
    volatile int* baz;
    foo(baz); // error: invalid conversion from ‘volatile int*’ to ‘int*’
}

为什么它很危险?对于 const 修饰符来说,删除它显然会破坏 const 的正确性;但是是否存在“易失性正确性”这样的东西呢?我无法弄清楚将指向易失性数据的指针作为指向非易失性数据的指针传递会如何导致问题。

编辑只是为了让你们知道为什么我首先使用易失性:许多Mac OS X的OSAtomic系列函数(用于原子增量、递减、加法、减法、比较和交换等)采用 易失性 参数。

In C++, volatile is treated the same way const is: passing a pointer to volatile data to a function that doesn't want the volatile modifier triggers a compile error.

int foo(int* bar) { /* snip */ }

int main()
{
    volatile int* baz;
    foo(baz); // error: invalid conversion from ‘volatile int*’ to ‘int*’
}

Why is it dangerous? It's obvious for the const modifier that removing it can break const correctness; but is there such a thing as "volatile correctness"? I can't figure out how passing a pointer to volatile data as a pointer to non-volatile data could cause problems.

EDIT Just so you guys know why I was using volatile in the first place: many of Mac OS X's OSAtomic family of functions (for atomic increments, decrements, additions, subtractions, compare and swap, etc.) takes volatile arguments.

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

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

发布评论

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

评论(5

つ低調成傷 2024-09-19 21:51:45

因为 易失性 修饰符意味着编译器必须注意按照 C 标准的“抽象机器”指定的方式实际执行易失性数据项的每次读/写。

当剥离易失性修饰符时,只要程序的行为“好像”发生了访问,只要程序控制流的单线程观点是这样,数据访问就可以被优化。担心的。换句话说,编译器可以将非易失性数据视为编译器并且只有编译器可以看到并可以修改该数据项(绝大多数情况都是这种情况)。

volatile 关键字告诉编译器其他东西(硬件或另一个执行线程)可以修改或查看该数据项,因此不允许编译器优化访问。

如果您可以在没有警告的情况下将指向易失性数据的指针传递给采用非易失性指针的函数,则该函数可能看不到可能发生的数据更改。如果您不关心这一点,您也许可以编写一个很好的、可移植的解决方法(取决于 foo() 处理数据的方式):

int foo(int* bar) { /* snip */ }

int main()
{
    volatile int* baz;

    int tmp = *baz;
    foo(&tmp);
    *baz = tmp;
}

Because the volatile modifier means that the compiler must take care to actually perform each read/write of the volatile data item exactly as the C standard's 'abstract machine' specifies.

When the volatile modifier is stripped away, the data accesses can be optimized away as long as the program behaves 'as if' the access occurred as far as the single-threaded viewpoint of the program flow of control is concerned. In other words, the compiler can treat a non-volatile piece of data as if the compiler and only the compiler can see and can modify the data item (which is the case in the vast majority of cases).

The volatile keyword tells the compiler that something else (hardware or another thread of execution) can modify or see that data item, so the compiler isn't permitted to optimize away accesses.

If you could pass a pointer to a volatile piece of data to a function that took a non-volatile pointer without warning, the function might not see a change in the data that might occur. If you don't care about that, you might be able to code up a nice, portable workaround (depending on what foo() does with the data):

int foo(int* bar) { /* snip */ }

int main()
{
    volatile int* baz;

    int tmp = *baz;
    foo(&tmp);
    *baz = tmp;
}
小嗷兮 2024-09-19 21:51:45

编译器不仅可以优化对非易失性变量的访问,而且可以预测/推测地更新它们,只要程序的顺序执行不受影响。

如果对易失性变量的虚假写入不会破坏您的设计,则它可能不需要在任何上下文中都是易失性的。

例如,C++03 编译器转换为完全合法

int result;
void sum_if_all_positive( std::array<N> ary )
{
    int sum = 0;
    result = -1;
    for( int i = 0; i < N; ++i ) {
        if (ary[i] < 0) return;
        sum += ary[i];
    }
    result = sum;
}

int result;
void sum_if_all_positive( std::array<N> ary )
{
    result = 0;
    for( int i = 0; i < N; ++i ) {
        if (ary[i] < 0) { result = -1; return; }
        result += ary[i];
    }
}

尽管这样的更改会提供比仅在少数具有廉价内存访问和很少寄存器的体系结构上注册 sum 更好的性能。我想到的是 Microchip PIC 体系结构.)

Not only can the compiler optimize away access to non-volatile variables, it can update them predictively/speculatively, as long as the sequential execution of the program is unaffected.

If spurious writes to your volatile variable don't break your design, it probably didn't need to be volatile in any context.

For example, it is perfectly legal for the C++03 compiler to transform

int result;
void sum_if_all_positive( std::array<N> ary )
{
    int sum = 0;
    result = -1;
    for( int i = 0; i < N; ++i ) {
        if (ary[i] < 0) return;
        sum += ary[i];
    }
    result = sum;
}

into

int result;
void sum_if_all_positive( std::array<N> ary )
{
    result = 0;
    for( int i = 0; i < N; ++i ) {
        if (ary[i] < 0) { result = -1; return; }
        result += ary[i];
    }
}

(Although such a change would provide better performance than enregistering sum only on a few architectures with cheap memory access and very few registers. The Microchip PIC architecture comes to mind.)

爺獨霸怡葒院 2024-09-19 21:51:45

好吧,在 foo() 中,编译器不再知道 baz (或更严格地说 bar)是易失性的,因此可能会尝试应用一些不适当的内容优化。

Well, in foo() the compiler no longer knows that baz (or more strictly bar) is volatile and so may attempt to apply some inappropriate optimisations.

遗心遗梦遗幸福 2024-09-19 21:51:45

易失性关键字意味着值应该每次从内存加载和存储到内存。

考虑一下代码:

int foo(int* bar) { 
    while(*bar){ 
        //Do something...
    }
}

int main()
{
    volatile int num = 1;
    volatile int* baz = #
    //Start a seperate thread to change *baz evenutally... 
    foo(baz);
}

当编译器看到 while 循环时,知道 bar 指向的总是 1。为什么每次都必须检查?如果每次都去检查,那就太浪费了。 易失性确保编译器确实每次都执行此检查,因此当其他线程更改值时,while循环退出。

The volatile keyword means that the value should be loaded and stored from/to memory every time.

Consider the code:

int foo(int* bar) { 
    while(*bar){ 
        //Do something...
    }
}

int main()
{
    volatile int num = 1;
    volatile int* baz = #
    //Start a seperate thread to change *baz evenutally... 
    foo(baz);
}

When the compiler sees the while loop, and knows that what is pointed to by bar is always going to be 1. Why should it have to check every time? It would be extremely wasteful to check every time. volatile makes sure that the compiler does do this check every time, so when the other thread changes the value, the while loop exits.

过气美图社 2024-09-19 21:51:45

是的,有这样的事情作为“不稳定的正确性”

它用于帮助确保多线程代码的安全性。

Yes, there is such a thing as "volatile correctness".

It is used to help ensure safety in multithreaded code.

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