volatile关键字如何影响静态const数组?

时间:2017-09-29 14:07:44

标签: c volatile

这是一位思想家,任何能够回答它的人都值得大家认可!这实际上是我要求更好理解的几个相关问题。

STM32 ARM Cortex平台的驱动程序包含以下代码:

static __I uint8_t APBAHBPrescTable[16] = {0, 0, 0, 0, 1, 2, 3, 4, 1, 2, 3, 4, 6, 7, 8, 9};

__I定义为:

#ifdef __cplusplus
  #define     __I     volatile                /*!< defines 'read only'   permissions      */
#else
  #define     __I     volatile const          /*!< defines 'read only'   permissions      */
#endif

我的程序是用GCC交叉编译器编译的C程序。因此,数组声明是有效的:

static volatile const uint8_t APBAHBPrescTable[16] = {0, 0, 0, 0, 1, 2, 3, 4, 1, 2, 3, 4, 6, 7, 8, 9};

问题1:

鉴于这是一个常量数组,为什么在这里使用volatile键工作?

我的理解是volatile关键字意味着数组的内容可以更改,但const意味着它们不能更改。

代码中唯一使用此数组的有三种用法:

tmp = RCC->CFGR & CFGR_PPRE1_Set_Mask;
tmp = tmp >> 8;
presc = APBAHBPrescTable[tmp];

当我转储tmppresc的值时,我发现tmp的值为4,presc的值为0.索引4是第5数组的元素,其值为1.此值没有其他访问或使用...完全...任何地方。

问题2:

声明它之间的值如何变化?

当我转储数组时,我看到它被零填充。

它可靠地发生......直到我从数组声明中删除__I。这让我觉得它不是缓冲区溢出。除此之外,我什么都想不到。

我认为volatile关键字是有原因的,除了我在中断处理程序中看到如下代码,据我所知,volatile关键字是多余的:

volatile uint32_t status = USART2->SR;

此变量是函数的本地变量,因此其他地方的代码永远不会更改。

======== EXTRA DETAIL ========

以下是相关代码段的带注释的反汇编。 (RCC_GetClocksFreq + 128)处的值为零,但在某些时候出现,以便将预分频器查找表的地址复制到其中:

0x000001d0 <+56>:    ldr     r1, [pc, #68]   ; (0x218 <RCC_GetClocksFreq+128>)
       ...
   tmp = RCC->CFGR & CFGR_PPRE1_Set_Mask;
   tmp = tmp >> 8;
0x000001de <+70>:    ldr     r4, [r2, #4]
0x000001e0 <+72>:    ubfx    r4, r4, #8, #3
   presc = APBAHBPrescTable[tmp];
0x000001e4 <+76>:    ldrb    r4, [r1, r4]
   RCC_Clocks->PCLK1_Frequency = RCC_Clocks->HCLK_Frequency >> presc;
0x000001e6 <+78>:    lsr.w   r4, r3, r4
0x000001ea <+82>:    str     r4, [r0, #8]

以上内容相同,但volatile const宏已替换为const

0x000001d0 <+56>:    ldr     r4, [pc, #68]   ; (0x218 <RCC_GetClocksFreq+128>)
       ...
   tmp = RCC->CFGR & CFGR_PPRE1_Set_Mask;
   tmp = tmp >> 8;
0x000001de <+70>:    ldr     r1, [r2, #4]
0x000001e0 <+72>:    ubfx    r1, r1, #8, #3
   presc = APBAHBPrescTable[tmp];
0x000001e4 <+76>:    ldrb    r1, [r4, r1]
   RCC_Clocks->PCLK1_Frequency = RCC_Clocks->HCLK_Frequency >> presc;
0x000001e6 <+78>:    lsr.w   r1, r3, r1
0x000001ea <+82>:    str     r1, [r0, #8]

它们基本相同。然而,以某种方式删除volatile关键字解决了这个问题!

3 个答案:

答案 0 :(得分:7)

  

我的理解是volatile关键字表示内容   数组可以改变,但const意味着它们不能改变。

volatile表示程序必须在每次使用时从内存中读取值。 const表示程序可能不会更改值,但环境(或“OS”)可能会更改。

这解释了您观察到的行为:如果没有volatile,编译器会认为可以读取一次值并多次使用它。

答案 1 :(得分:1)

实时时钟可以使用volatile const构造来发布当前时间:

volatile const struct tm TheTimeNow;
  • 您的程序无法更改时钟,因此它应为const
  • 时钟在您和编译器的后面永久地和神奇地滴答,因此最好使用volatile强制编译器始终获取当前时间而不是旧时间戳。

RTC可能在地址空间中有一个自己的部分,它显示当前时间。

答案 2 :(得分:0)

首先,感谢所有引起我回答的评论和答案。

当变量定义时没有&#34; volatile&#34;关键字,它被放入二进制文件的只读部分。

当使用&#34; volatile&#34;定义变量时关键字,它与所有其他变量放在二进制文件的同一部分。

我最近发现3个缓冲区溢出,我相信还有其他的。很多代码编写得不是很好。很可能是当&#34; volatile&#34;指定了关键字,该变量放置在内存中,使其容易受到缓冲区溢出的影响。完全没有理由将此特定变量标记为volatile,因此简单的解决方法是删除该关键字。正确的解决方法是这样做,并追踪缓冲区溢出并修复它。