这是一位思想家,任何能够回答它的人都值得大家认可!这实际上是我要求更好理解的几个相关问题。
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];
当我转储tmp
和presc
的值时,我发现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关键字解决了这个问题!
答案 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,因此简单的解决方法是删除该关键字。正确的解决方法是这样做,并追踪缓冲区溢出并修复它。