C語言中的一些關鍵字
阿新 • • 發佈:2019-02-18
volatile關鍵字
volatile關鍵字以前用的很少,但是在進行nRF51822定時器程式設計時,碰到在如下程式段,結合程式分析volatile關鍵字的作用。在分析程式之前,先來介紹一下volatile關鍵字。在這裡是根據《C語言深度解剖》一書的理解來寫的。 volatile本身的意思就是易變的、不穩定的意思。而C語言在編譯的時候往往會對程式進行優化,但是對一些特殊的地址進行優化可能會得到適得其反的效果(在這裡僅僅是自己得到的理論,還沒有驗證與碰到過)。因此,在遇到這個關鍵字宣告的變數,編譯器對訪問該變數的程式碼就不再進行優化,從而可以提供對特殊地址的穩定訪問。下面是關於volatile關鍵字對比的兩個例子。#include "nrf51.h" #include "nrf_gpio.h" #include "led.h" #include "time.h" #include <stdbool.h> #include <stdint.h> /** * @brief Function for timer initialization. */ static volatile NRF_TIMER_Type * timer_init(timer_t timer) { volatile NRF_TIMER_Type * p_timer; // 開始16 MHz晶振. NRF_CLOCK->EVENTS_HFCLKSTARTED = 0; NRF_CLOCK->TASKS_HFCLKSTART = 1; // 等待外部振盪器啟動 while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0) { // Do nothing. } switch (timer) { case TIMER0: p_timer = NRF_TIMER0; break; case TIMER1: p_timer = NRF_TIMER1; break; case TIMER2: p_timer = NRF_TIMER2; break; default: p_timer = 0; break; } return p_timer; } /** @brief Function for using the peripheral hardware timers to generate an event after requested number of milliseconds. * * @param[in] timer Timer to be used for delay, values from @ref p_timer * @param[in] number_of_ms Number of milliseconds the timer will count. * @note This function will power ON the requested timer, wait until the delay, and then power OFF that timer. */ void nrf_timer_delay_ms(timer_t timer, uint_fast16_t volatile number_of_ms) { volatile NRF_TIMER_Type * p_timer = timer_init(timer); if (p_timer == 0) { while(true) { // Do nothing. } } p_timer->MODE = TIMER_MODE_MODE_Timer; // 設定為定時器模式 p_timer->PRESCALER = 9; // Prescaler 9 produces 31250 Hz timer frequency => 1 tick = 32 us. p_timer->BITMODE = TIMER_BITMODE_BITMODE_16Bit; // 16 bit 模式. p_timer->TASKS_CLEAR = 1; // 清定時器. // With 32 us ticks, we need to multiply by 31.25 to get milliseconds. p_timer->CC[0] = number_of_ms * 31; p_timer->CC[0] += number_of_ms / 4; p_timer->TASKS_START = 1; // Start timer. while (p_timer->EVENTS_COMPARE[0] == 0) { // Do nothing. } p_timer->EVENTS_COMPARE[0] = 0; p_timer->TASKS_STOP = 1; // Stop timer. } /** @} */
int i=10;
int j=i; //1語句
int k=i; //2語句
此時編譯器對程式碼進行優化,由於1語句和2語句之間變數i並沒有被賦值,也就是i的值沒有發生改變,所以編譯器在1語句時從記憶體中取出i的值賦給j之後,這個值並沒有被丟掉,而是在2語句時繼續用這個值給k賦值。編譯器不會生成出彙編程式碼重新從記憶體裡取i的值,這樣提高了效率。
另一個例子:
此時,volatile告訴編譯器,i是隨時可能發生變化的,每次使用它的時候必須從記憶體中取出i的值,因而編譯器生成的彙編程式碼會重新從i的地址處讀取資料放在k中。 如果i是一個暫存器變數,表示一個埠資料或者是多個執行緒共享資料,那麼就容易出錯,所以說volatile可以保證對特殊地址的穩定訪問。 總結: volatile提醒編譯器它後面所定義的變數隨時都有可能改變,因此編譯後的程式每次需要儲存或讀取這個變數的時候,都會直接從變數地址中讀取資料。如果沒有volatile關鍵字,則編譯器可能優化讀取和儲存,可能暫時使用暫存器中的值,如果這個變數由別的程式更新了的話,將出現不一致的現象。 簡單來說,就是自己所定義的變數在程式執行過程中一直會變,如果希望這個值被正確處理,就需要每次從記憶體中去讀這個值,這樣就不會有錯誤了,volatile關鍵字就是這個作用。 一般來說,volatile關鍵字用在如下的幾個地方: 1、中斷服務程式中修改的供其他程式檢測的變數需要加volatile; 2、多工環境下各任務間共享的標誌應該加volatile; 3、儲存器對映的硬體暫存器通常也要加volatile說明,因此每次對它的讀寫都可能有不同意義。 瞭解了這麼多關於volatile關鍵字的知識以及用法,現在來分析nRF51822定時器程式。 在nRF51822中,用volatile這個型別修飾符來修飾NRF_TIMER_TYPE *p_timer這個指向函式的指標變數,也就是說明這個指標變數是易變的,編譯器在訪問該變數的時候不再進行優化,而是直接從變數地址中讀取資料。 在此,對程式按照執行流程進行分析。當主函式這樣呼叫函式nrf_timer_delay_ms(TIMER0,TIMER_DELAY_MS)(TIMER_DELAY_MS為在標頭檔案中的巨集定義,為1000UL)時,程式進入子函式中。在此個人感覺子函式形參變數number_of_ms用volatile修飾符修飾沒用,因為TIMER_DELAY_MS本來就為一巨集定義變數volatile int i=10; int j=i; //3語句 int k=i; //4語句