什么是extern volatile指针

什么是extern volatile指针。

extern volatile uint32 *ptr; 

在这里,* ptr的行为是什么? 这究竟意味着什么?

而且,什么时候应该使用?

我曾试图谷歌,但没有得到任何满意的答案,关于这个组合的信息不多。

外部 和易失性关键字都可以独立考虑。 这些关键字中的每一个的角色不与另一个关键字相互作用,因此可以独立地详细说明它们中的每一个,如下所述。

extern告诉编译器ptr的实际定义是在另一个模块中(另一个.c )。 基本上,编译器处理ptr的方式没有太大的变化 – 有extern只是告诉编译器它不必在内存中为ptr保留一些空间,因为它在另一个.c其他位置完成,并且它的实际内存位置将是稍后由链接器给出。

  extern uint32 *ptr; 

如果省略extern ,编译器不会抱怨。 但是,稍后, 链接器在尝试链接所有对象模块以构建最终的可执行程序时,将抛出错误“ ptr已定义两次”(因为它已在另一个.c定义)。

  uint32 *ptr; 

volatile告诉编译器ptr所在的内存位置可能被某些外部事件更改/修改,并且它(编译器)不应该依赖于某些效率优化,例如考虑到ptr的值在一些范围内不会改变这样的事件可以是异步中断,当CPU执行上述范围时发生,并且修改ptr值。

通常(在评论中使用优化的C的虚拟汇编代码),REGx是CPU寄存器,我们对变量y不太感兴趣…

  int x = 10; int func() { int y; // REG4 printf("%d\n", x); // Set REG3 = memory(x) and display x x += 2; // Add 2 to REG3 y = x * x; // REG4 = REG3 * REG3 printf("%d %d\n", x, y); // Do printf(..., REG3, REG4) x += 5; // REG3 = REG3 + 5 // memory(x) = REG3 (save register to memory) return y; // return REG4 } 

应显示10, 12, 144 。 为了提高效率(内存访问比寄存器访问更昂贵),编译器会将x的值存储在内部CPU寄存器(RE​​G3)中,并在func中安全地使用它直到它存储x的新值(它的结尾)全局var)到x内存位置。 最后x是17。

但是想象一下程序比这更复杂,并且每分钟都有一个时钟中断,减去10到x 。 如果中断会发生什么……

  void inter_call_by_timer_every_minute() { x -= 10; } 

…发生在func之后,就在printf("%d\n", x); 线? func在REG3(10)中有x ,加2(12)并最后加5(17)并将REG3结果存储到x存储位置(17)。 这是错误的,因为编译器优化已经隐藏了中断效果(-10),因为它在最后将值从REG3存储到存储器(x),而忽略了由中断完成的减法 。 正确的结果是: x最初是10,在func中的第一个printf之后中断减去10(0),然后添加2,然后是5.结果7。

添加挥发性

  volatile int x = 10; 

将让编译器避免在func中进行x优化

  int func() { int y; // REG4 printf("%d\n", x); // display memory(x) x += 2; // memory(x) += 2 y = x * x; // REG4 = memory(x) * memory(x) printf("%d %d\n", x, y); // Do printf(..., memory(x), REG4) x += 5; // memory(x) += 5 return y; // return REG4 } 

并始终从内存中读取x的值。 在第一个printf之后, inter_call_by_timer_every_minute中断的结果是x == 7。

我会通过关键字解释我所知道的方式。 extern表示变量被定义为在定义范围之外的某处使用。 例如,它可以在头文件中定义并在.c文件中使用。

volatile意味着该变量的修改应与外部世界保持一致。 这意味着应将所有更新提交到主内存,以便共享相同执行空间的其他线程可以看到它。

extern关键字用于声明一个在其他地方定义的全局变量(这意味着它在其他一些.c文件中定义)。

例如,在项目中考虑两个.c文件acbc 。 因为全局变量是在ac定义的,并且该变量可以在该文件中定义的所有函数中访问。 如果我们想在第二个文件bc访问同一个全局变量,那么该变量应该在bc声明为extern

ac文件如下

 int flag = 0; int main() { ....... func1(); printf("\nflag value is %d\n", flag). ....... } 

bc文件如下

 extern int flag; void func1(); { ..... flag = 10; ..... } 

volatile关键字用于通知编译器在生成可执行指令时避免进行任何类型的优化。

 int flag = 0; int main() { while(flag == 0); printf("\nflag value is %d\n", flag); return 0; } 

考虑上面的程序,所有编译器都会优化while(flag == 0); while(1); 。 因为在代码中, while循环之前没有where flag值被更新。 因此,如果该变量值被其他硬件更新,那么它将永远不会反映在程序执行中。 因此,如果我们将变量声明为如下所示的volatile ,编译器将不会对该变量执行任何优化,并且该程序的行为将如预期的那样。

 volatile int flag = 0; 

但是如果没有办法让程序变量的值被其他硬件更新,那么就不需要将该变量声明为volatile 。 因为对于valatile变量,CPU需要对访问该变量的每个指令执行I / O操作。 对于一个永远不会被其他硬件更新的变量,需要考虑这种性能影响。

Extern意味着它在别处定义 – 可能在头文件中。 易失性是编译器的信息,它不应该尝试优化它。