浅析C语言中的const与volatile关键字

C语言const与volatile

1. const

声明一个只读变量。

2. volatile

告诉编译器不要去优化volatile修饰的代码,并且每次直接从数据源头读取数据,不允许将数据缓存到寄存器。

注意:对volatile变量的操作不是原子性的。

注意:volatile在c语言中和在java语言中的含义是不同的,本文重要阐述在c语言中的作用

2.1 volatile的用途

2.1.1 寄存器访问(每次都直接从寄存器地址获取数据)

访问寄存器必须使用volatile修饰符。

  1. 对可读可写的寄存器使用volatile,防止编译器对多次操作该寄存器的代码进行优化(一般是编译器开启了比较高的优化等级,才会执行这个代码优化)
define GPIOB_ODR (*(volatile unsigned long *)0x40010C10UL)

example:

GPIOB_ODR = 1;
GPIOB_ODR = 2;
GPIOB_ODR = 3;

如果不使用volatile修饰符,并且开启 O3 优化,则编译器可能会将上面的代码优化成:

// 编译器会任务前面两个只是中间过程,所以会被优化掉
// 但是对寄存器的每一次操作都是有现实意义的
GPIOB_ODR = 3;
  1. 对只读的寄存器使用volatile const,每次都直接从源地址读取数据,并且不允许对该地址进行写操作
define GPIOB_IDR (*(volatile const unsigned long *)0x40010C08UL)

example:

read_value = GPIOB_IDR;
print_to_screen(read_value);

read_value = GPIOB_IDR;
print_to_screen(read_value);

read_value = GPIOB_IDR;
print_to_screen(read_value);

同样的,如果不使用volatile修饰符,并且开启 O3 优化,则编译器可能会将上面的代码优化成:

read_value = GPIOB_IDR;
print_to_screen(read_value);

以上实验,可以通过观察编译后的汇编代码验证。

2.1.2 信号处理

比如下面的例子,本意是:主程序等待外部中断触发,然后将标志位变量flag置位,从而使得主程序得以继续运行。

/* main.c */

// global variable
uint8_t flag = 0;

int main()
{
    while(!falg);

    // do something else

    return 0;
}

// 中断处理函数

void Exception_IRQHandler(void)
{
    flag=1;
}

但是如果flag不使用volatile修饰符,并且编译器开启 O3 优化,那么程序将会被优化成:

  while (!flag);
 800020a:	4b05      	ldr	r3, [pc, #20]	; (8000220 <main+0x2c>)
 800020c:	781b      	ldrb	r3, [r3, #0]
 800020e:	b903      	cbnz	r3, 8000212 <main+0x1e>
 ; 一直在这里循环,而不会每次都去重新判断条件是否被满足
 8000210:	e7fe      	b.n	8000210 <main+0x1c>    

用C语言表示就相当于:

int main()
{
    while(ture);

    return 0;
}

程序将永远无法继续向下运行,不管外部中断是否改变flag变量。

在STM32中,在进行串口发送的时候,通常会阻塞等待发送完毕:

USART_SendData(USART1, (uint8_t) cmd);

// 阻塞等待串口发送完毕
while (__HAL_UART_GET_FLAG(USART1, USART_FLAG_TXE) != SET){}	
#define     __IO    volatile             /*!< Defines 'read / write' permissions */

typedef struct
{
  __IO uint32_t SR;         /*!< USART Status register,                   Address offset: 0x00 */
  __IO uint32_t DR;         /*!< USART Data register,                     Address offset: 0x04 */
  __IO uint32_t BRR;        /*!< USART Baud rate register,                Address offset: 0x08 */
  __IO uint32_t CR1;        /*!< USART Control register 1,                Address offset: 0x0C */
  __IO uint32_t CR2;        /*!< USART Control register 2,                Address offset: 0x10 */
  __IO uint32_t CR3;        /*!< USART Control register 3,                Address offset: 0x14 */
  __IO uint32_t GTPR;       /*!< USART Guard time and prescaler register, Address offset: 0x18 */
} USART_TypeDef;


#define __HAL_UART_GET_FLAG(__HANDLE__, __FLAG__) (((__HANDLE__)->Instance->SR & (__FLAG__)) == (__FLAG__))

可以看到是经过volatile修饰的

更多精彩内容,请关注微信公众号“边缘智能实验室”

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C语言volatile关键字是用来声明一个变量为“易变”的。这意味着该变量的值可以在程序执行期间随时发生变化,可能由于硬件或者其他并行执行的线程的影响。使用volatile关键字可以告诉编译器不要对该变量进行优化,以确保每次访问该变量时都从内存读取最新的值。这对于需要频繁读写硬件寄存器或者在多线程环境下共享的变量非常有用。 参考资料: C语言深度解析专栏—C语言关键字详解第五篇 C语言再学习 -- 关键字volatile_聚优致成的博客-CSDN博客 更多关键字在下面博客链接 C语言关键字详解(一)auto、register关键字 C语言关键字详解(二)带你全面了解 static C语言关键字详解(三)数据类型与sizeof关键字 C语言关键字详解(四)带你全面了解 const 关键字<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [C语言关键字详解(五)带你全面了解 volatile 关键字](https://blog.csdn.net/m0_62391199/article/details/123746218)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [C语言volatile 关键字](https://blog.csdn.net/qq_58264156/article/details/127416196)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值