博客專欄

EEPW首頁 > 博客 > 嵌入式軟件開發(fā)常用的關(guān)鍵字和運(yùn)算符

嵌入式軟件開發(fā)常用的關(guān)鍵字和運(yùn)算符

發(fā)布人:美男子玩編程 時(shí)間:2023-12-18 來源:工程師 發(fā)布文章

1

volatile關(guān)鍵字

volatile是一個(gè)特征修飾符,提醒編譯器它后面所定義的變量隨時(shí)都有可能改變,因此編譯后的程序每次需要存儲(chǔ)或讀取這個(gè)變量的時(shí)候,告訴編譯器對(duì)該變量不做優(yōu)化,都會(huì)直接從變量內(nèi)存地址中讀取數(shù)據(jù),從而可以提供對(duì)特殊地址的穩(wěn)定訪問。

常用場景:中斷服務(wù)與主程序共享變量。示例代碼如下:

















//volatile uint8_t flag=1;uint8_t flag=1;void test(void){    while(flag)    {        //do something    }}//interrupt service routinevoid isr_test(void){    flag=0;}

如果沒使用volatile定義flag,可能在優(yōu)化后test陷入死循環(huán),因?yàn)閠est里使用的flag并沒修改它,開啟優(yōu)化后,編譯器可能會(huì)固定從某個(gè)內(nèi)存取值。

2

const關(guān)鍵字

const 是 constant 的縮寫,意思是“恒定不變的”,它是定義常變量的關(guān)鍵字。

通常有4種用法。

1、修飾變量

采用const修飾變量,即變量聲明為只讀,保護(hù)變量值以防被修改。




const int i = 1;或者int const i=1;

變量i具有只讀特性,不能夠被更改;若想對(duì)i重新賦值,如i = 10,屬于錯(cuò)誤操作。

2、修飾數(shù)組

數(shù)組元素與變量類似,具有只讀屬性,不能被更改,一旦更改,編譯時(shí)就會(huì)報(bào)錯(cuò)。



const int array[5] = {1,2,3,4,5};array[0] = array[0]+1; //錯(cuò)誤,array是只讀的,禁止修改

使用大數(shù)組存儲(chǔ)固定的信息,例如查表(表驅(qū)動(dòng)法的鍵值表),可以使用const節(jié)省ram。編譯器并不給普通const只讀變量分配空間,而是將它們保存到符號(hào)表中,無需讀寫內(nèi)存操作,程序執(zhí)行效率也會(huì)提高。

3、修飾指針

C語言中const修飾指針要特別注意,共有兩種形式,一種是用來限定指向空間的值不能修改;另一種是限定指針不可更改。






int i = 1;int j = 2;const int *p1 = &i;int* const p2 = &j;

上面定義了兩個(gè)指針p1和p2,區(qū)別是const后面是指針本身還是指向的內(nèi)容。

在定義1中const限定的是*p1,即其指向空間的值不可改變,若改變其指向空間的值如*p1=10,則程序會(huì)報(bào)錯(cuò);但p1的值是可以改變的,對(duì)p1重新賦值如p1=&k是沒有任何問題的。

在定義2中const限定的是指針p2,若改變p2的值如p2=&k,程序?qū)?huì)報(bào)錯(cuò);但*p2,即其所指向空間的值可以改變,如*p2=20是沒有問題的,程序正常執(zhí)行。

4、 修飾函數(shù)參數(shù)

const關(guān)鍵字修飾函數(shù)參數(shù),對(duì)參數(shù)起限定作用,防止其在函數(shù)內(nèi)部被修改,所限定的函數(shù)參數(shù)可以是普通變量,也可以是指針變量。






void fun(const int i){    ……    i++; //對(duì)i的值進(jìn)行了修改,程序報(bào)錯(cuò)}

常用的函數(shù)如strlen。


size_t strlen(const char *string);

const在庫函數(shù)中使用非常普遍,是一種自我保護(hù)的安全編碼思維。

3

static關(guān)鍵字

1、static修飾全局變量,該變量只在本文件內(nèi)被訪問,不能在其他文件被直接訪問。

2、static修飾函數(shù),該函數(shù)只能在本文件內(nèi)被訪問,不能被其他文件訪問。但是可以通過嵌套的方式調(diào)用,變相的封裝的表現(xiàn)。

3、static修飾局部變量,更改該局部變量的生命周期。

  • 生命周期:將臨時(shí)變量的生命周期變成全局變量的生命周期。

  • 作用域不變:作用域仍然是在本代碼塊內(nèi)。

4

struct與union

可以使用struct結(jié)構(gòu)體來存放一組不同類型的數(shù)據(jù)。




struct 結(jié)構(gòu)體名{    結(jié)構(gòu)體所包含的變量或數(shù)組};

結(jié)構(gòu)體是一種集合,它里面包含了多個(gè)變量或數(shù)組,它們的類型可以相同,也可以不同,每個(gè)這樣的變量或數(shù)組都稱為結(jié)構(gòu)體的成員,通常我們使用結(jié)構(gòu)體定義和解析協(xié)議,如下所示:














// WiFi接收數(shù)據(jù)幀,控制切換模式#pragma pack(1)typedef struct receive_data_mode_t{    uint8_t device_head;        // 數(shù)據(jù)幀頭:0XA0+功能碼(FUNCTION_ID3),A款產(chǎn)品智能插座    uint16_t device_len;        // 數(shù)據(jù)包總長度    uint16_t device_id;         // 節(jié)點(diǎn)ID 0X0001~0XFFFE    char software_version[15];  // 軟件版本 SMART_SW_A1_1.0 A款產(chǎn)品軟件1.0版本    char hardware_version[15];  // 硬件版本 SMART_HW_A1_1.0 A款產(chǎn)品硬件1.0版本    uint8_t switch_mode;        // 切換模式 0:運(yùn)行模式,1:配置模式,2:節(jié)點(diǎn)升級(jí),3:節(jié)點(diǎn)重啟    uint16_t crc;               // 校驗(yàn)位}ReceiveData_Mode_t;#pragma pack()

union共用體關(guān)鍵字,定義union下面的成員變量共享一塊內(nèi)存,每一個(gè)成員在任一時(shí)刻有且只有一個(gè)成員使用此塊內(nèi)存。




union 共用體名{    成員列表};

結(jié)構(gòu)體和共用體的區(qū)別在于:結(jié)構(gòu)體的各個(gè)成員會(huì)占用不同的內(nèi)存,互相之間沒有影響;而共用體的所有成員占用同一段內(nèi)存,修改一個(gè)成員會(huì)影響其余所有成員。

通常使用共用體做一些標(biāo)志位操作,例如以下示例,可以非常靈活的訪問Val中的bit位。
















typedef union {     BYTE Val;     struct __packed     {        BYTE b0:1;        BYTE b1:1;        BYTE b2:1;        BYTE b3:1;        BYTE b4:1;        BYTE b5:1;        BYTE b6:1;        BYTE b7:1;    } bits;}BYTE_VAL, BYTE_BITS;

或者使用共用體實(shí)現(xiàn)單字節(jié)與多字節(jié)的轉(zhuǎn)化和拼接,如下所示:




























#include "stdio.h"typedef struct{    union    {        struct        {            unsigned char low;            unsigned char high;        };        unsigned short result;    };}test_t;int main(int argc, char *argv[]){    test_t hello;    hello.high=0x12;    hello.low=0x34;    printf("result=%04Xrn",hello.result);//輸出 result=1234     return 0;}

5

預(yù)定義標(biāo)識(shí)符

一般編譯器都支持預(yù)定義標(biāo)識(shí)符,這些標(biāo)識(shí)符結(jié)合printf等打印信息幫助程序員調(diào)試程序是非常有用的,一般編譯器會(huì)自動(dòng)根據(jù)用戶指定完成替換和處理。

常用的預(yù)定義標(biāo)識(shí)符如下所示:






__FILE__    //表示編譯的源文件名__LINE__   //表示當(dāng)前文件的行號(hào)__FUNCTION__  //表示函數(shù)名__DATE__  //表示編譯日期__TIME__   //表示編譯時(shí)間

在Debug打印日志時(shí)候經(jīng)常會(huì)用到,如下所示:


printf("file:%s,line:%d,date:%s,time:%s",__FILE__,__LINE__,__DATE__,__TIME__);

6

#與##

#:是一種運(yùn)算符,用于帶參宏的文本替換,將跟在后面的參數(shù)轉(zhuǎn)成一個(gè)字符串常量。

##:是一種運(yùn)算符,是將兩個(gè)運(yùn)算對(duì)象連接在一起,也只能出現(xiàn)在帶參宏定義的文本替換中。















#include "stdio.h"#define TO_STR(s) #s#define COMB(str1,str2) str1##str2int main(int argc, char *argv[]){    int UART0= 115200;    printf("UART0=%dn", COMB(UART, 0));//字符串合并為變量UART0    printf("%sn", TO_STR(3.14));//將數(shù)字變成字符串    return 0;}

7

void 與 void*關(guān)鍵字

void表示的是無類型,不能聲明變量或常量,但是可以把指針定義為void類型,如void* ptr。void* 指針可以指向任意類型的數(shù)據(jù),在C語言指針操作中,任意類型的數(shù)據(jù)地址都可轉(zhuǎn)為void* 指針。因?yàn)橹羔槺举|(zhì)上都是unsigned int。

常用的內(nèi)存塊操作庫函數(shù):



void * memcpy( void *dest, const void *src, size_t len );void * memset( void *buffer, int c, size_t num);

數(shù)據(jù)指針為void* 類型,對(duì)傳入任意類型數(shù)據(jù)的指針都可以操作。另外其中memcpy第二個(gè)參數(shù),const現(xiàn)在也如前文所述,拷貝時(shí)對(duì)傳入的原數(shù)據(jù)內(nèi)容禁止修改。

特殊說明,指針是不能使用sizeof求內(nèi)容大小的,在ARM系統(tǒng)固定為int 4字節(jié)。對(duì)于函數(shù)無輸入?yún)?shù)的,也盡量加上void,如下所示:


void fun(void);

8

weak關(guān)鍵字

一般簡化定義如下所示:


#define _WEAK __attribute__((weak))

函數(shù)名稱前面加上__WEAK屬性修飾符稱為“弱函數(shù)”,類似C++的虛函數(shù)。鏈接時(shí)優(yōu)先鏈接為非weak定義的函數(shù),如果找不到則再鏈接帶weak函數(shù)。











_WEAK void fun(void)  {      //do this}  //不在同一個(gè).c,兩同名函數(shù)不能在同一個(gè)文件void fun(void)  {      //do that}

這種自動(dòng)選擇的機(jī)制,在代碼移植和多模塊配合工作的場景下應(yīng)用較多。例如前期移植代碼,需要調(diào)用某個(gè)接口fun,但當(dāng)前該接口不存在或者未移植完整使用,可以使用weak關(guān)鍵字定義為空函數(shù)先保證編譯正常。

后續(xù)移植完成實(shí)現(xiàn)了fun,即軟件中有2個(gè)fun函數(shù)沒有任何錯(cuò)誤,編譯器自動(dòng)會(huì)識(shí)別使用后者。當(dāng)然也粗暴的#if 0屏蔽對(duì)fun的調(diào)用,但要確保后續(xù)記得放開。

*博客內(nèi)容為網(wǎng)友個(gè)人發(fā)布,僅代表博主個(gè)人觀點(diǎn),如有侵權(quán)請(qǐng)聯(lián)系工作人員刪除。



關(guān)鍵詞: 嵌入式

相關(guān)推薦

技術(shù)專區(qū)

關(guān)閉