易失性指针指向非易失性数据

Volatile pointer points to non volatile data

我明白声明

int *volatile ptr;

表示指针本身是volatile

int a=10;
int *volatile ptr=&a;

现在 ptra 都在更新。访问ptr时是否会导致返回a的旧值等潜在问题?

阐明volatile指针在我的场景中的用例:

  1. 从中断上下文到我正在传递的任务上下文进行通信 循环队列(指针数组)的地址,它将 从任务上下文中访问。
  2. 此队列中的指针被声明为可变的。
  3. 在队列中的索引 x 处,存在指针 y,它指向 变量 a.
  4. 现在如果我写新的队列指针(例如指针 z 指向 到中断上下文中索引 x 处的变量 b)。
  5. 当我在任务上下文中读取索引 x 时,因为队列被声明为 volatile,指针不会被优化。

我的疑问是因为指针指向的不是易变的,编译器是否有可能针对索引 x 进行了优化,并且在索引 x 处引用指针时它将指向变量 a 而不是变量 b

如果 ptr 和 ptr 指向的 int 都由不同的线程或您的硬件更新,则必须像这样声明 ptr:

int volatile * volatile ptr=&a;

或者:

volatile int * volatile ptr=&a;

通常在嵌入式编程中,正如@Lundin 指出的那样,地址不会改变,所以这就足够了:

volatile int * ptr=&a;

或者:

int volatile * ptr=&a;

int* volatile只表示指针本身是volatile。 pointed-at 数据不是 volatile-qualified,这意味着以下情况可能会导致优化器错误:

int a=10;
int* volatile ptr=&a;

int main()
{
  for(;;)
  {
    printf("%d\n", *ptr);
  }
}

void some_ISR (void)
{
  a = something;
}

编译器注意不要假定 ptr 不会在循环中的每一圈都指向相同的地址,但除此之外它可以自由假定:"aha, after reading ptr it is still pointing at a I see. It has not been updated and I know it is 10"。理论上,编译器可以自由生成如下伪机器码:

val = *ptr

forever
{
  ptr = (update it by reading from memory)
  if ptr != previous
    val =*ptr

  print val        
  previous = ptr
}

如果编译器可以将先前的值隐藏在 CPU 寄存器等中,这样的优化可能是有意义的。假设它可以通过这种方式摆脱大量的 printf 开销,那将是一个主要的优化。

所以,不,这不会保护您免受不正确的优化。为此,请改用 volatile int*。或者如果指针本身也可能改变,volatile int* volatile.


To communicate from interrupt context to task context I am passing addresses to circular queue (which is array of pointers) which will be accessed from task context.

只有当指针本身被中断更改为指向别处时,这才有意义。同样,这并不意味着 pointed-at 数据将被更新。但是当你写 "The pointers in this queue are declared volatile." 时,你很好,假设这意味着指针是 volatile* type 不是 type*volatile.

与整个问题无关,您还需要防止 non-atomic 访问共享变量。 volatile 不给那个。

通过 volatile 指针对 a 的任何使用也会使解引用变易变(因为指针的值未缓存,无论如何都必须进行解引用)但是,更好的是使两者都不可缓存,因此一种可能的实现方式是:

volatile int a;
volatile int * volatile ptr = &a;

请三思,因为您没有将 a 声明为 volatile,如果您这样做了,您会收到警告:

pru.c

 #include <stdio.h>
 int main()
 {
    volatile int a;
    int * volatile ptr = &a;
 }

并输出

$ make pru
pru.c:5:17: warning: initializing 'int *volatile' with an expression of type 'volatile int *' discards qualifiers
      [-Wincompatible-pointer-types-discards-qualifiers]
    int * volatile ptr = &a;
                   ^     ~~
1 warning generated.

正如您从警告中看到的那样,丢弃的限定符指的是指向的值。如果 ptr 被声明为非 volatile,则会发出相同的警告:

pru2.c

 #include <stdio.h>
 int main()
 {
    volatile int a;
    int * ptr = &a;
 }

$ make pru2
cc -O -pipe  pru.c  -o pru
pru.c:5:8: warning: initializing 'int *' with an expression of type 'volatile int *' discards qualifiers
      [-Wincompatible-pointer-types-discards-qualifiers]
        int * ptr = &a;
              ^     ~~
1 warning generated.