# C static、const、volatile ###### tags: `C` * 常見的內容涵蓋 const, static, volatile, call by value, call by reference, bitwise operation, pointer, stack * struct、enum、typedef ## <span class="red">define</span> <style> .red { color: red; } </style> * #define 是給 preprocessor 看的prepprocessor 看完執行完 #define 及置換後, 會把這些 #define 的東西拿掉, 所以 C 編譯器是看不到這些 * #define 的 * #define 的作用範圍是整個檔案 (從 #define 開始一直到 #undef或檔案結束) * typedef * typedef 是給 C 編譯器看的. * typedef 的作用範圍: 和變數宣告遵守相同的規則 ## <font class="red">typedef</font> * typedef 關鍵字用於命名使用者定義的物件。在程式碼中經常需要多次宣告結構。 * 如果不使用 typedef 來定義它們,每次宣告都需要以 struct/enum 關鍵字開始,這就使得程式碼的可讀性變得很重。 * typedef 是 type definition的縮寫,型態的定義。 * typedef 已經有的資料型態重新定義其他名稱,也就是說定義自己的資料型態。 * typedef 資料型態 識別字; ## <span class="red">static</span> * c 語言中static 關鍵字有兩個作用,一是檔案作用域,二是函式作用域。 1. 檔案作用域關鍵字static 的作用是,以static 申明的全域性變數、函式不得被其他檔案所引用。 2. 函式內部靜態變數,只會被初始化一次,而且變數儲存在全域性資料段中而不是函式棧中,所以其生命期會一直持續到程式退出。 * 舉個回撥函式的栗子:雖然counting()函式多次回撥自身,但是靜態整型變數 count 並沒有每次都被初始化,而是隻初始化了一次,達到了計數目的。 ## <span class="red">inline</span> * inline只是建議編譯器要做行內編譯,實際上編譯器不一定會執行。通常要 -O1 以上才有效果 * inline : 宣告可被引用的函式 (為C++語言時代新的語法,會有編譯器限制) * #define在程式行數多的時候比較不好解讀,inline 則反之 ## <span class="red">static inline</span> * 所以說當inline沒被compiler接受的時候.... * inline -> 變成普通函式 -> 外部可用extern呼叫 * static inline -> 變成普通static函式 -> 外部不可用extern * extern inline -> 變成普通函式 -> 外部可用extern呼叫 * 當inline被compiler接受時: * inline -> 變成inline -> 外部不可用extern * static inline -> 變成inline -> 外部不可用extern * extern inline -> 變成inline+普通函式 -> 外部可用extern呼叫 ## <span class="red">volatile</span> * volatile英文意思是可變的。在C語言中,一個定義為volatile 的變數是說這變數很可能會被意想不到地改變,因此需要小心對待。 * 也就是說,優化器在用到這個變數時必須每次重新從虛擬記憶體中讀取這個變數的值,而不是使用儲存在暫存器裡的備份。 ## <span class="red">const</span> * 資料型別被const修飾的變數在 " 初始化 " 之外不能再被賦值 * 只可以讀,不能寫 * 可以看待成一個唯讀(read-only)的屬性 * 只要const在左邊常數在左邊內容物都不可變動 * 除非使用指標,指向的內容是可改變的 * 一般範例 ```= int a = 3; const int b =5; //唯讀 數值不可改 const int c; //編譯可過,但沒有初數 a = 4; b = 6; //編譯失敗 ``` * 矩陣範例 ```= int a[3] = {3 , 4, 5}; const int b[3] = {5 , 6, 7}; a [0] = 4; b [0] = 6; //編譯失敗,因為const整數是不能被修改 ``` * 指標範例 ```= const chat *str = "text"; srt[0] = 0; //編譯失敗 ``` * 綜合範例 ```= const int * a = &b; // 指標指向的內容不可改變 int const * a = &b; // 同上 int * const a = &b; // 常數指標,即指標本身的值是不可改變的,但指向的內容是可改變的 const int * const a = &b; // 指向常數的常數指標,即指標本身與指向的內容都是不可改變的 ``` ### <span class="red">const static and static const</span> * 此方式 可以在 .h 使用 ``` = static const uint8_t gpio_port[] = {0, 2, 2, 2, 2, 2, 2, 5, 5, 5, 2, 2, 2, 4, 2, 2, 0, 0, 0, 0, 5, 5, 5, 1, 5, 5, 1, 2, 2, 2, 3, 3, 3, 0, 5, 5, 3, 3, 3, 2, 3, 3, 3, 3, 3, 5, 0}; static const uint8_t gpio_bit[] = {12, 1, 2, 3, 4, 5, 14, 12, 13, 14, 9, 10, 11, 11, 12, 13, 11, 2, 3, 13, 2, 5, 6, 10, 8, 9, 15, 6, 15, 7, 1, 4, 0, 5, 15, 16, 5, 6, 7, 8, 9, 10, 11, 12, 15, 18, 15}; ``` ```= static GPIO_TypeDef * const gpio_port[] = {GPIOA, GPIOA, GPIOA, GPIOA, GPIOA, GPIOA, GPIOA, GPIOA, GPIOA, GPIOB, GPIOB, GPIOB, GPIOB, GPIOB, GPIOB, GPIOB, GPIOB, GPIOB, GPIOB, GPIOB}; static const uint16_t gpio_pin[] = {GPIO_PIN_0, GPIO_PIN_1, GPIO_PIN_2, GPIO_PIN_3, GPIO_PIN_4, GPIO_PIN_8, GPIO_PIN_11, GPIO_PIN_12, GPIO_PIN_15, GPIO_PIN_1, GPIO_PIN_2, GPIO_PIN_3, GPIO_PIN_4, GPIO_PIN_5, GPIO_PIN_6, GPIO_PIN_7, GPIO_PIN_12, GPIO_PIN_13, GPIO_PIN_14, GPIO_PIN_15}; ``` ## <font color="#f00">enum</font> ![](https://i.imgur.com/0JTU4lB.png) * enum 是一種特殊的常數定義方式,enum基本上只是整數值,其名稱為變數(用於在數值上)。 * 藉由enum型態宣告,取代較不易記憶的一組整數常數 * 第一種是給常數名稱 * 第二種是給數字然後轉換成列舉的型別 ```= enum 型態名稱 { 常數 1 , 常數 2 , 常數 3 , 常數 n }; ``` ```= enum 型態名稱 常數 1 , 常數 2, 常數 3, 常數 n; ``` ### <span class="red">typedef enum</span> * 使用 typedef enum 定義包含命名整數常量的物件的定製型別 * 另外 typedef enum 大部分會應再用statemachine(如範例二、三). #### <span class="red">typedef enum 方法 一 </span> * 固定抓取數值 ```= typedef enum { read = 0, blule = 1, gread = 2, yeall = 3 }MACHINE_STATE; ``` ```= int main(void) { int input1; MACHINE_STATE state; state = input1; switch(state) { case read: printf("read: read \r\n"); break; case blule: printf("blule: blule \r\n"); break; case gread: printf("gread: gread \r\n"); break; case yeall: printf("yeall: yeall \r\n"); break; } } ``` #### <span class="red">typedef enum 方法 二 </span> * 使用inline包一個function的方式來使用狀態機流程 ```= typedef enum { SYSTEM_MODE_NONE = -1, SYSTEM_MODE_IS_OFF = 0, ON_Delay = 1, SYSTEM_MODE_IS_ON = 2, OFF_EVENT_DELAY = 3, HARD_OFF_DELAY = 4, SYSTEM_Batt_low_status = 5, LOW_HARD_DELAY = 6, SYSTEM_MODE_HARD_OFF_DELAY = 7, SYSTEM_MODE_POWER_BUTTON = 8, SYSTEM_Batt_Low_Power = 9, SYSTEM_MODE_IGN_WAIT = 10, } SYSTEM_MODE_T; ``` ```=+ volatile static SYSTEM_MODE_T system_mode = SYSTEM_MODE_IS_OFF; /** intialize system_mode to SYSTEM_IS_OFF */ inline SYSTEM_MODE_T get_system_mode(void){ return system_mode; } ``` ```=+ SYSTEM_MODE_T sys_mode; sys_mode = get_system_mode(); ignotion_enable = gpio_read(IGN_ENABLE); if ( ignotion_enable == 1) { while(1) { switch(mode) { case SYSTEM_MODE_IS_OFF : sys_mode = ON_Delay; break; case ON_Delay: sys_mode = SYSTEM_MODE_IS_ON; break; case SYSTEM_MODE_IS_ON: if (low_power) { sys_mode = SYSTEM_Batt_Low_Power; break; } if (gpio_read(PG_DCIN) == GPIO_PIN_SET) { _system_shutdown(); sys_mode = SYSTEM_MODE_IGN_WAIT; break; } if (gpio_read(V12_SB_D_PG) == GPIO_PIN_SET) { _system_shutdown(); sys_mode = SYSTEM_MODE_IGN_WAIT; break; } if (gpio_read(PG_MAIN_PWR) == GPIO_PIN_RESET) { _system_shutdown(); sys_mode = SYSTEM_MODE_IGN_WAIT; break; } break; case SYSTEM_MODE_IGN_WAIT: sys_mode = SYSTEM_MODE_IS_OFF; break; case SYSTEM_MODE_HARD_OFF_DELAY: for (i = 0; i < vpm_reg.hard_off_dly ; i++) { /* Maximum HARD_OFF_DELAY */ if (gpio_read(PG_MAIN_PWR) == GPIO_PIN_RESET) { _system_shutdown(); break; } HAL_Delay(1000); } sys_mode = SYSTEM_MODE_IGN_WAIT; break; case SYSTEM_Batt_low_status: if (_Batt_Low_power_status()) { HAL_Delay(1000); } else { sys_mode = ON_Delay; } break; case LOW_HARD_DELAY: sys_mode = SYSTEM_MODE_IS_OFF; break; case OFF_EVENT_DELAY: sys_mode = SYSTEM_MODE_IS_OFF; break; case HARD_OFF_DELAY: sys_mode = SYSTEM_MODE_IS_OFF; break; } } } ``` #### <span class="red">typedef enum 方法 三 </span> ## <span class="red">Struct</span> ![](https://i.imgur.com/R99iUKD.png) * 以下定義結構資料型態 * struct 結構名稱 變數1 變數2 變數3 * struct 結構型態 結構陣列名稱[元素] * struct 結構型態 *結構指標名稱 ### <span class="red">Struct 型態</span> ```= typedef union { unsigned char byte[4]; unsigned long word; }StringWordType; typedef stuct { unsigned short data1; unsigned short data2; unsigned char data3; unsigned char data4; unsigned char data5; unsigned long data6; unsigned long data7; unsigned short data8; StringWordType data9; }DataValue; typedef struct cur_type { int cur_state; //current state int old_state; //old state }cur_type_t; ``` ```= cur_type_t get_cur = { .cur_state = SYSTEM_MODE_NONE, .old_state = SYSTEM_MODE_NONE }; ``` #### <span class="red">Struct 範例1</span> * 最常見的方式是將struct列很多的資料型態或者當成buffer使用 * 本範例將function用成struct ```= typedef struct BOARD_VDATA { float Pertial_Pressure_12V_24V; float Pertial_Pressure_37V_48V; float voltages_12V_24V; float voltages_37V_48V; } BOARD_VDATA_T; BOARD_VDATA_T *read_voltages(void); static BOARD_VDATA_T adc_vdata; ``` ```= float get_adc_12V_Pertial_Pressure(void) { float value_12V_24V = read_adc_value(ADC_CHANNEL_6); float D4_value = (float) value_12V_24V * ADC_Partial_Pressure; return D4_value; } inline float adc_read_12v() { return get_adc_12V_24V(); } BOARD_VDATA_T *read_voltages(void) { adc_vdata.Pertial_Pressure_12V_24V = get_adc_12V_Pertial_Pressure(); adc_vdata.Pertial_Pressure_37V_48V = get_adc_37V_Pertial_Pressure(); adc_vdata.voltages_12V_24V = get_adc_12V_24V(); adc_vdata.voltages_37V_48V = get_adc_37V_48V(); return &adc_vdata; } ``` ```= BOARD_VDATA_T *pVdata = read_voltages(); dbg_printf("adc12 :%.2fv \r\n", pVdata->voltages_12V_24V); dbg_printf("adc37 :%.2fv \r\n", pVdata->voltages_37V_48V); ``` ### <span class="red">typedef struct</span> ```= typedef struct BOARD_VDATA { float supply_48V; float supply_12V; float supply_3P3; float supply_5V; float cam1_48V; float cam2_48V; float cam3_48V; float cam4_48V; } BOARD_VDATA_T; void NTC7904_update_voltages(void); BOARD_VDATA_T* NTC7904_read_voltages(void); ``` ```= static BOARD_VDATA_T vdata; ``` ```= void NTC7904_update_voltages(void) { vdata.supply_48V = get_ntc7904_voltage48v(); vdata.supply_12V = get_ntc7904_voltage12v(); vdata.supply_3P3 = get_ntc7904_voltage3_3v(); vdata.supply_5V = get_ntc7904_voltage5v(); vdata.cam1_48V = get_ntc7904_voltage48v_cam1(); vdata.cam2_48V = get_ntc7904_voltage48v_cam2(); vdata.cam3_48V = get_ntc7904_voltage48v_cam3(); vdata.cam4_48V = get_ntc7904_voltage48v_cam4(); } BOARD_VDATA_T* NTC7904_read_voltages(void) { return &vdata; } ``` ## <span class="red">Unions</span> ![](https://i.imgur.com/vEe5qkO.png) ### <span class="red">Code Function</span> ``` #include <stdio.h> // Declaration of union is same as structures union test { int x, y; }; int main() { // A union variable t union test t; t.x = 2; // t.y also gets value 2 printf("After making x = 2:\n x = %d, y = %d\n\n", t.x, t.y); t.y = 10; // t.x is also updated to 10 printf("After making y = 10:\n x = %d, y = %d\n\n", t.x, t.y); return 0; } ``` ## <span class="red">函數</span> ### <span class="red">strcpy</span> * strcpy提供了字符串的複製。即strcpy只用於字符串複製,並且它不僅複製字符串內容之外,還會複製字符串的結束符。 ``` char *strcpy(char *dest, const char *src) ``` ### <span class="red">memcpy</span> ``` void *memcpy(void *str1, const void *str2, size_t n) ``` * memcpy提供了一般內存的複製。即memcpy對於需要複製的內容沒有限制,因此用途更廣。 ``` = #include <stdio.h> #include <string.h> int main () { const char src[50] = "http://www.gitbook.net/html"; char dest[50]; printf("Before memcpy dest = %s ", dest); memcpy(dest, src, strlen(src)+1); printf("After memcpy dest = %s ", dest); return(0); } ``` ### <span class="red">memset</span> ``` void *memset(void *str, int c, size_t n) ``` * memset:作用是在一段記憶體塊中填充某個給定的值,它是對較大的結構體或陣列進行清零操作的一種最快方法。 ```= #include <stdio.h> #include <string.h> int main () { char str[50]; strcpy(str,"This is string.h library function"); puts(str); memset(str,'$',7); puts(str); return(0); } ``` ## 參考 * [ const、static、volatile關鍵字使用說明](https://www.itread01.com/content/1548856807.html) * [C 語言中的靜態變數](https://www.delftstack.com/zh-tw/howto/c/static-variable-in-c/) * [ C 語言中使用 typedef enum](https://www.delftstack.com/zh-tw/howto/c/c-typedef-enum/) * [C 語言中的對映或 Struct](https://www.delftstack.com/zh-tw/howto/c/map-in-c/) * [C語言 memset()用法及代碼示例](https://vimsky.com/zh-tw/examples/usage/memset-c-example.html) * [Enumeration (or enum) in C](https://www.geeksforgeeks.org/enumeration-enum-c/?ref=leftbar-rightbar) * [Structures in C](https://www.geeksforgeeks.org/structures-c/?ref=lbp) * [Union in C](https://www.geeksforgeeks.org/union-c/?ref=lbp)