易失性指针指向非易失性数据
Volatile pointer points to non volatile data
我明白声明
int *volatile ptr;
表示指针本身是volatile
int a=10;
int *volatile ptr=&a;
现在 ptr
和 a
都在更新。访问ptr
时是否会导致返回a
的旧值等潜在问题?
阐明volatile指针在我的场景中的用例:
- 从中断上下文到我正在传递的任务上下文进行通信
循环队列(指针数组)的地址,它将
从任务上下文中访问。
- 此队列中的指针被声明为可变的。
- 在队列中的索引 x 处,存在指针 y,它指向
变量 a.
- 现在如果我写新的队列指针(例如指针 z 指向
到中断上下文中索引 x 处的变量 b)。
- 当我在任务上下文中读取索引 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.
我明白声明
int *volatile ptr;
表示指针本身是volatile
int a=10;
int *volatile ptr=&a;
现在 ptr
和 a
都在更新。访问ptr
时是否会导致返回a
的旧值等潜在问题?
阐明volatile指针在我的场景中的用例:
- 从中断上下文到我正在传递的任务上下文进行通信 循环队列(指针数组)的地址,它将 从任务上下文中访问。
- 此队列中的指针被声明为可变的。
- 在队列中的索引 x 处,存在指针 y,它指向 变量 a.
- 现在如果我写新的队列指针(例如指针 z 指向 到中断上下文中索引 x 处的变量 b)。
- 当我在任务上下文中读取索引 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.