什么是extern volatile指针

Art*_*rti 10 c volatile

什么是extern volatile指针.

extern volatile uint32 *ptr;
Run Code Online (Sandbox Code Playgroud)

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

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

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

Rin*_*g Ø 14

双方的extern挥发性关键字可以单独考虑.这些关键字中的每一个的角色不与另一个关键字交互,因此可以独立地详细说明它们中的每一个,如下所述.

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

  extern uint32 *ptr;
Run Code Online (Sandbox Code Playgroud)

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

  uint32 *ptr;
Run Code Online (Sandbox Code Playgroud)

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
  }
Run Code Online (Sandbox Code Playgroud)

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

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

  void inter_call_by_timer_every_minute() {
     x -= 10;
  }
Run Code Online (Sandbox Code Playgroud)

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

添加挥发性

  volatile int x = 10;
Run Code Online (Sandbox Code Playgroud)

将让编译器避免在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
  }
Run Code Online (Sandbox Code Playgroud)

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

  • 需要注意的是,C 标准中并不能保证像“+=”这样的运算符是原子的,而“x * x”当然不是原子的。因此,虽然“易失性”确实告诉编译器不要缓存读取,但它不会对原子性做任何事情。此外,在这种情况下,如果在“x * x”中的两次读取之间发生“x -= 10”,则添加“易失性”可能会导致“y”*不*最终成为任何整数的平方。这可能是比跳过单个计时器递减更糟糕的结果。 (2认同)