【问题标题】:Is read to local volatile variable always guaranteed in C?在 C 中是否始终保证读取到局部 volatile 变量?
【发布时间】:2022-08-18 15:10:48
【问题描述】:

是否始终保证读取到本地 volatile 变量?

我有一个对结构成员执行一些操作的函数。结构成员不是易失性的,并且在函数运行时可能会发生变化(嵌入式应用程序、中断)。如果值在读取期间发生变化(复制到局部变量),这没有问题,但在执行多个 if 语句时它们不应发生变化。

#include <string.h>
#include <stdio.h>

typedef struct {
    int wptr;
    int rptr;
} rw_t;

void
use_it(const rw_t* rw) {
    volatile int r, w;

    /* It is OK if rw->rptr or rw->wptr get changed during these copy operation */
    r = rw->rptr;
    w = rw->wptr;

    /* I must ensure that r and w are actually local references,
       therefore a copy to local shall be done first */
    if (r > w) {
        printf(\"R is more than W\\r\\n\");
    } else if (r < w) {
        printf(\"W is more than R\\r\\n\");
    } else {
        printf(\"R and W are equal\\r\\n\");
    }
}

使用-Os 使用 ARM-GCC none-eabi 10.2.1 进行编译似乎可以正常工作。但这是否保证始终适用于任何编译器和任何优化?

最小可复制示例:https://godbolt.org/z/nvocrsrzE

  • 看起来您真正想要的是将参数声明为 volatile 指针:void use_it(const volatile rw_t *rw)...
  • \"结构成员不是易失性的,并且在函数运行时可能会改变(嵌入式应用程序、中断)。\" --> 所以这些成员是易挥发的没有volatile,没有atomic?然后代码在撒谎,r = rw-&gt;rptr; 冒着 UB 的风险,因为在阅读 rw-&gt;rptr; 时没有原子保护。请详细说明“如果值在读取过程中发生变化(复制到局部变量)”是正确的。
  • @chux 如果 CPU 需要多条指令从内存中读取变量并且它在中间被中断并且新值被加载(在中断例程中)到实际内存中,这没关系,我们可以使用旧值或新值(取决于它何时被打断了)。真正重要的是,在所有 if-elseif-else 语句中,rw 不会改变。因此,无论优化设置如何,我都必须确保变量真正被复制到局部变量中。
  • 我真的无法理解。在r = rw-&gt;rptr;w = rw-&gt;wptr; 之后,rw 都是本地的副本.这意味着无论rw 成员发生什么,rw 的值都不会改变。如果rw-&gt;w 的值在r = rw-&gt;rptr;w = rw-&gt;wptr; 之间变化,您最多可以有一个竞争条件。但是如果没有一点同步(例如互斥锁),这是无法解决的。
  • @tilz0R 我认为您可以保证r, wif() 期间不会改变。然而,仍然是狡猾的任务r = rw-&gt;rptr; w = rw-&gt;wptr;

标签: c


【解决方案1】:

对象是否具有自动、静态或动态存储持续时间并不重要。 volatile 向编译器表明对象容易产生副作用,每次使用时都会从其存储位置读取它,每次修改时都会存储它。

int foo(volatile int x)
{
    return x+x+x+x;
}


foo:
        sub     sp, sp, #8
        str     r0, [sp, #4]
        ldr     r3, [sp, #4]
        ldr     r1, [sp, #4]
        ldr     r2, [sp, #4]
        add     r3, r3, r1
        ldr     r0, [sp, #4]
        add     r3, r3, r2
        add     r0, r3, r0
        add     sp, sp, #8
        bx      lr

但在你的情况下,你需要一些完全不同的东西

void
use_it(volatile const rw_t* rw) {
    int r, w;

    r = rw->rptr;  //guaranteed to read from memory
    w = rw->wptr;

//further usage guaranteed to be using local r & w

use_it:
        ldr     r2, [r0, #4]  //reads from memory
        ldr     r3, [r0]      //reads from memory
        cmp     r2, r3        //from now uses local variables (stored in registers is optimizations are on
        push    {r4, lr}
        bgt     .L9
        ldrlt   r0, .L10
        ldrge   r0, .L10+4
        bl      puts
        pop     {r4, lr}
        bx      lr

【讨论】:

  • 为什么是DV?任何解释的人
  • 所以秘密是通过在参数中添加volatile const rw_t* rw,以保证复制然后使用局部变量?
  • @chux-ReinstateMonica 我不关心 r = rw-&gt;rptr; w = rw-&gt;wptr; - 我只关心在进一步的 if 语句中使用局部变量。巴斯塔。
  • @tilz0R 它完全符合您的要求。
  • @tilz0R 是的,这就是秘密。
最近更新 更多