電子產(chǎn)業(yè)一站式賦能平臺

PCB聯(lián)盟網(wǎng)

搜索
查看: 23|回復(fù): 0
收起左側(cè)

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

[復(fù)制鏈接]

552

主題

552

帖子

3302

積分

四級會員

Rank: 4

積分
3302
跳轉(zhuǎn)到指定樓層
樓主
發(fā)表于 2023-12-4 12:00:00 | 只看該作者 |只看大圖 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
7 p3 s( t7 q: G/ T) ?, G
點(diǎn)擊上方藍(lán)色字體,關(guān)注我們  _5 @9 O0 S" q7 O+ G# G. o# U7 x4 J
( v, y2 T- z$ ~9 @; U9 Y9 C
1$ @. p. i% O$ {+ w
volatile關(guān)鍵字
% |5 P! h; P5 G6 f& O) U& Mvolatile是一個(gè)特征修飾符,提醒編譯器它后面所定義的變量隨時(shí)都有可能改變,因此編譯后的程序每次需要存儲或讀取這個(gè)變量的時(shí)候,告訴編譯器對該變量不做優(yōu)化,都會直接從變量內(nèi)存地址中讀取數(shù)據(jù),從而可以提供對特殊地址的穩(wěn)定訪問。9 K" |0 J( B8 f8 X$ O9 N: m

3 c/ I+ E; t; M. z# s常用場景:中斷服務(wù)與主程序共享變量。示例代碼如下:
( \1 ?2 w- A; }( {1 w7 C0 p! u# N/ x% ~, A1 }
  • //volatile uint8_t flag=1;uint8_t flag=1;5 @9 Z8 `4 M! t) r
    void test(void){    while(flag)    {        //do something    }}
    - E6 ?1 X3 \% ]& e//interrupt service routinevoid isr_test(void){    flag=0;}! b6 {& I; A! a4 i- s8 J0 s- W
    如果沒使用volatile定義flag,可能在優(yōu)化后test陷入死循環(huán),因?yàn)閠est里使用的flag并沒修改它,開啟優(yōu)化后,編譯器可能會固定從某個(gè)內(nèi)存取值。
    $ A4 [( Q2 [, G! Q9 L
    $ @) ^* X9 B, v, R/ |25 |# B5 D! J/ s, y8 V8 X
    const關(guān)鍵字
    8 G/ [$ {; m+ \& M' O, P- }: dconst 是 constant 的縮寫,意思是“恒定不變的”,它是定義常變量的關(guān)鍵字。
    / v. |' n  y1 e. j( F2 o通常有4種用法。3 `" A/ F5 }* O* u) k

    . _; Y$ Q0 L9 a1 E* ~

    ! s7 B4 Z* ~" Z) V7 V3 I- S+ F& ]1、修飾變量
    - D6 A! ?6 D$ }  t$ _! X
    " l8 p8 i7 F) @% e6 P采用const修飾變量,即變量聲明為只讀,保護(hù)變量值以防被修改。
    # U2 @! H8 @  E$ f  f
    - S7 B& t, k$ [7 V; \
  • const int i = 1;或者int const i=1;
    * h4 s" j4 l& ]4 p' R變量i具有只讀特性,不能夠被更改;若想對i重新賦值,如i = 10,屬于錯(cuò)誤操作。' K  j& [% ~) p! i# k) Q8 o5 C6 {
    ; f% R' I" R* r+ p" M

    7 }8 i3 x1 f6 P9 \2、修飾數(shù)組( }% q. |1 H2 e) {

    ) ?3 l4 ~2 i, @7 o* s8 I& W數(shù)組元素與變量類似,具有只讀屬性,不能被更改,一旦更改,編譯時(shí)就會報(bào)錯(cuò)。
    4 Q. `- c, d" A" T% I4 R2 i% y+ I& v" b% A& ^! {7 W+ U
  • const int array[5] = {1,2,3,4,5};array[0] = array[0]+1; //錯(cuò)誤,array是只讀的,禁止修改
    ! |. N; C- |/ v1 U使用大數(shù)組存儲固定的信息,例如查表(表驅(qū)動法的鍵值表),可以使用const節(jié)省ram。編譯器并不給普通const只讀變量分配空間,而是將它們保存到符號表中,無需讀寫內(nèi)存操作,程序執(zhí)行效率也會提高。
    ( s* M2 a2 z% i% K8 [4 A. i
    & W1 {, v0 E0 s5 |# P7 B& F
    4 y' i; Q2 w9 U( A) H
    3、修飾指針- ]; p  ]2 n( P& e0 c3 h4 V
    + r4 p; W) Y5 o+ C. t
    C語言中const修飾指針要特別注意,共有兩種形式,一種是用來限定指向空間的值不能修改;另一種是限定指針不可更改。; }0 F6 t4 k  i1 D( Z' O% Y

    ) e& ~# S1 \+ X4 K  H/ X
  • int i = 1;int j = 2;  |. R" d' v  D; l. O# g) r
    const int *p1 = &i;int* const p2 = &j;" y$ f. H9 t" @* m  g
    上面定義了兩個(gè)指針p1和p2,區(qū)別是const后面是指針本身還是指向的內(nèi)容。
    , z8 ^0 b+ B) J: T
    ; V2 W( m. d& z3 A在定義1中const限定的是*p1,即其指向空間的值不可改變,若改變其指向空間的值如*p1=10,則程序會報(bào)錯(cuò);但p1的值是可以改變的,對p1重新賦值如p1=&k是沒有任何問題的。
    % o! r# c- f" f5 t2 e3 c5 `9 s& Y0 p) E/ n3 D6 M
    在定義2中const限定的是指針p2,若改變p2的值如p2=&k,程序?qū)䦂?bào)錯(cuò);但*p2,即其所指向空間的值可以改變,如*p2=20是沒有問題的,程序正常執(zhí)行。
    9 L7 ?' J. ~* c( s1 r7 k4 ~6 S0 x3 z6 l% `
    0 {1 C" H: I0 v$ J# o0 H
    4、 修飾函數(shù)參數(shù)- C* p$ n  }4 @
    const關(guān)鍵字修飾函數(shù)參數(shù),對參數(shù)起限定作用,防止其在函數(shù)內(nèi)部被修改,所限定的函數(shù)參數(shù)可以是普通變量,也可以是指針變量。
    3 k8 Q* k1 j1 W$ O9 o
    & d3 G/ C* w! O' p- k; F
  • void fun(const int i){    ……    i++; //對i的值進(jìn)行了修改,程序報(bào)錯(cuò)}
    5 m- e7 Z" b' F! G8 h常用的函數(shù)如strlen。
    / \: N6 n4 R* |+ S- S( O; V, r3 ~3 M) [  T4 B
  • size_t strlen(const char *string);
    # }% d" k2 f4 \) r3 p# g$ r4 Cconst在庫函數(shù)中使用非常普遍,是一種自我保護(hù)的安全編碼思維。+ U* C# }2 U& A
    3' q/ Y$ }" ^) y5 k) H) x
    static關(guān)鍵字
    $ m8 L* D+ Y, C# u. R: a& [. P1、static修飾全局變量,該變量只在本文件內(nèi)被訪問,不能在其他文件被直接訪問。
    ; `/ v; ~- I2 ~8 Z2 s3 T& ~( Z( d' p6 W6 R: K) E
    2、static修飾函數(shù),該函數(shù)只能在本文件內(nèi)被訪問,不能被其他文件訪問。但是可以通過嵌套的方式調(diào)用,變相的封裝的表現(xiàn)。
    # V- Z# C, Z# j
      z1 d) n; k' E3 |& e3 r7 K3、static修飾局部變量,更改該局部變量的生命周期。
    9 i" V7 E% E' C; p1 m
  • 生命周期:將臨時(shí)變量的生命周期變成全局變量的生命周期。
  • 作用域不變:作用域仍然是在本代碼塊內(nèi)。
    8 j$ j2 I( w! p- U: c8 k' r

    1 Y4 H0 ?! V2 D; a4
    8 a) b5 M# x7 d: L+ Q! Nstruct與union
    1 T/ z1 d7 b2 V+ b6 r% \可以使用struct結(jié)構(gòu)體來存放一組不同類型的數(shù)據(jù)。" F* m" f' o' }, s

    0 c) ?0 E7 Q% R3 b4 @1 A
  • struct 結(jié)構(gòu)體名{    結(jié)構(gòu)體所包含的變量或數(shù)組};! L# p  X0 ]7 b- ]4 N: o
    結(jié)構(gòu)體是一種集合,它里面包含了多個(gè)變量或數(shù)組,它們的類型可以相同,也可以不同,每個(gè)這樣的變量或數(shù)組都稱為結(jié)構(gòu)體的成員,通常我們使用結(jié)構(gòu)體定義和解析協(xié)議,如下所示:! \4 T8 s$ \# [9 X

    ! ~) H# X2 A& [$ C
  • // 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)升級,3:節(jié)點(diǎn)重啟    uint16_t crc;               // 校驗(yàn)位}ReceiveData_Mode_t;#pragma pack()( R! w# L5 b+ V" @% e9 \( }0 F7 _
    union共用體關(guān)鍵字,定義union下面的成員變量共享一塊內(nèi)存,每一個(gè)成員在任一時(shí)刻有且只有一個(gè)成員使用此塊內(nèi)存。2 K  I5 v7 A; c- \: P* x& ^" \% K
    2 h1 f, j) W8 x, O! R
  • union 共用體名{    成員列表};( Q" Y0 z' V, D: \
    結(jié)構(gòu)體和共用體的區(qū)別在于:結(jié)構(gòu)體的各個(gè)成員會占用不同的內(nèi)存,互相之間沒有影響;而共用體的所有成員占用同一段內(nèi)存,修改一個(gè)成員會影響其余所有成員。* [; }# y- M  V/ A  I* {2 L
    通常使用共用體做一些標(biāo)志位操作,例如以下示例,可以非常靈活的訪問Val中的bit位。+ a  P# N* w# N
    " G; A' }5 P7 z* T
  • 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;
    ! z  C; C) q% l; d& y或者使用共用體實(shí)現(xiàn)單字節(jié)與多字節(jié)的轉(zhuǎn)化和拼接,如下所示:3 w0 {; q& x: `# x, G9 O+ p2 S

    2 P; `( O$ N, z% r9 r/ ^' G
  • #include "stdio.h"% }* Q+ r/ f1 X$ A7 [
    typedef struct{    union    {        struct        {            unsigned char low;            unsigned char high;        };        unsigned short result;    };}test_t;9 U' C0 K) v. l* t) |* p& V
    int main(int argc, char *argv[]){    test_t hello;- H5 i2 g) ~% |" ?! S
        hello.high=0x12;    hello.low=0x34;, L& I6 f+ o4 U5 u  l  R% g6 J# a0 J
        printf("result=%04X\r
    : q; V9 B6 U3 {  O",hello.result);//輸出 result=1234
    : |9 a2 `( d; s; q    return 0;}
    & I0 r# h9 K% N) a+ i7 ~3 m% Z. |9 q
    % }) Q3 c# D+ y6 z5 J( I( O59 D, h) u" g# D6 a
    預(yù)定義標(biāo)識符4 j+ L& H+ N# m% R, y9 u0 V0 A" s
    一般編譯器都支持預(yù)定義標(biāo)識符,這些標(biāo)識符結(jié)合printf等打印信息幫助程序員調(diào)試程序是非常有用的,一般編譯器會自動根據(jù)用戶指定完成替換和處理。( d3 R; x1 Y; s* H8 c3 C) n
      R. J( a: {& m: M
    常用的預(yù)定義標(biāo)識符如下所示:
    8 c" d( w2 x5 y( E( ~! `( i1 E$ [4 Y: y- K- F! Z0 s! i1 x
  • __FILE__    //表示編譯的源文件名__LINE__   //表示當(dāng)前文件的行號__FUNCTION__  //表示函數(shù)名__DATE__  //表示編譯日期__TIME__   //表示編譯時(shí)間8 T6 i" s  l$ q% ~$ ^! m- F& L
    在Debug打印日志時(shí)候經(jīng)常會用到,如下所示:
    1 F. @+ l  c3 B1 d1 R0 A% q) ^9 J- i. p! L% D
  • printf("file:%s,line:%d,date:%s,time:%s",__FILE__,__LINE__,__DATE__,__TIME__);: ?% b# W8 ]0 g: _9 o3 D
    , l5 |* R9 E0 T/ Q
    6
    7 g4 C% F+ ], z0 {. k# P4 d#與### y  _: h- J) H7 t; j! D
    #:是一種運(yùn)算符,用于帶參宏的文本替換,將跟在后面的參數(shù)轉(zhuǎn)成一個(gè)字符串常量。
    6 ]2 l& }( D9 g$ W  a) k1 @! _& W. U5 \8 f
    ##:是一種運(yùn)算符,是將兩個(gè)運(yùn)算對象連接在一起,也只能出現(xiàn)在帶參宏定義的文本替換中。- R& ^; P. q" k9 u, ]

    ) x, @5 l, l! u
  • #include "stdio.h"3 ]$ L* O" V9 F2 j# k% i
    #define TO_STR(s) #s#define COMB(str1,str2) str1##str21 D4 J. Z% }3 r
    int main(int argc, char *argv[]){    int UART0= 115200;
    , w' e$ C0 U  U7 Q9 ?, j( g    printf("UART0=%d
    2 q) [- Q; }0 \", COMB(UART, 0));//字符串合并為變量UART0    printf("%s
    ! W3 e" J0 v1 |; k+ U; w  M", TO_STR(3.14));//將數(shù)字變成字符串
    + c, M6 m6 R6 V4 Y2 k3 _    return 0;}
    * x% L9 j2 J# k$ E& f
    # l, z+ g8 p- X  ]- ?7
    + A( H: S! W# b. I; nvoid 與 void*關(guān)鍵字
    / v' _6 o, }- w: ]! ~+ `void表示的是無類型,不能聲明變量或常量,但是可以把指針定義為void類型,如void* ptr。void* 指針可以指向任意類型的數(shù)據(jù),在C語言指針操作中,任意類型的數(shù)據(jù)地址都可轉(zhuǎn)為void* 指針。因?yàn)橹羔槺举|(zhì)上都是unsigned int。) {0 E' d- _* h- q

    * w$ t2 A1 p9 v- e( a2 \常用的內(nèi)存塊操作庫函數(shù):$ W  p3 T& e: c: r* n

    6 d( _  e4 H5 z- Z* W% `! U
  • void * memcpy( void *dest, const void *src, size_t len );void * memset( void *buffer, int c, size_t num);
    9 @8 I/ u0 @8 S, a* q數(shù)據(jù)指針為void* 類型,對傳入任意類型數(shù)據(jù)的指針都可以操作。另外其中memcpy第二個(gè)參數(shù),const現(xiàn)在也如前文所述,拷貝時(shí)對傳入的原數(shù)據(jù)內(nèi)容禁止修改。% F! y/ Z3 c, g1 w
    * \& ~' h7 E, [% }; H& t, B
    特殊說明,指針是不能使用sizeof求內(nèi)容大小的,在ARM系統(tǒng)固定為int 4字節(jié)。對于函數(shù)無輸入?yún)?shù)的,也盡量加上void,如下所示:: C: e0 l& H, d* ~" T/ n: Y
    ) P* l: h  I2 _5 ?, i) O
  • void fun(void);; j- E2 R4 @9 j

    4 a# x6 B2 k( c% y0 o% W1 t4 z80 g. P, H3 n' L# w2 J( I! X7 y' B
    weak關(guān)鍵字: ]  W1 a( V# X8 a1 p( O% {5 s
    一般簡化定義如下所示:
    # z1 V9 A1 {* L# c0 H& X2 E1 ~7 i! }9 C% I2 T5 s
  • #define _WEAK __attribute__((weak))
    ) u, C( H- J$ T# U函數(shù)名稱前面加上__WEAK屬性修飾符稱為“弱函數(shù)”,類似C++的虛函數(shù)。鏈接時(shí)優(yōu)先鏈接為非weak定義的函數(shù),如果找不到則再鏈接帶weak函數(shù)。
    9 o3 _! \3 Z1 z7 ~$ M" s# c1 z2 i8 c9 U' ~! l
  • _WEAK void fun(void)  {      //do this}  
    2 P) H" A. n/ X! b( L/ o$ m//不在同一個(gè).c,兩同名函數(shù)不能在同一個(gè)文件void fun(void)  {      //do that}- o: z2 h7 B; `% b( a
    這種自動選擇的機(jī)制,在代碼移植和多模塊配合工作的場景下應(yīng)用較多。例如前期移植代碼,需要調(diào)用某個(gè)接口fun,但當(dāng)前該接口不存在或者未移植完整使用,可以使用weak關(guān)鍵字定義為空函數(shù)先保證編譯正常。. |' i! p1 K; p) z. A) r3 U

    : }& D. Z. B# d7 O后續(xù)移植完成實(shí)現(xiàn)了fun,即軟件中有2個(gè)fun函數(shù)沒有任何錯(cuò)誤,編譯器自動會識別使用后者。當(dāng)然也粗暴的#if 0屏蔽對fun的調(diào)用,但要確保后續(xù)記得放開。/ [0 n" J$ h" B. T: n/ j

    ' e% M7 h, s$ y% Z9 Q: z
      W  U. S8 x- a+ ^$ E% A往期推薦什么是內(nèi)存碎片?
    ' Z/ Q5 ]- {, q2 W0 r. D詳解UDS CAN診斷:什么是UDS(ISO 14229)診斷?  y/ o1 c5 r" W$ b$ r( X
    磁耦合共振無線供電裝置
    $ f# m8 w. i. N. }9 N+ YC語言:十六進(jìn)制(HEX)和浮點(diǎn)類型(float、double)轉(zhuǎn)換
    & K. X! ^0 |$ Q  zHarmonyOS 分布式多端應(yīng)用一站式開發(fā)平臺(DevEco Studio 安裝)
    . J1 A$ ?, |- Z4 b. a
    : V- Z6 `3 b6 E; w& }% p

    - U. q4 t+ F; S; V  n
    & e. _" _5 B* {& p9 _- D6 D- F
    2 f8 h5 d: \# A8 i點(diǎn)擊閱讀原文,更精彩~
  • 發(fā)表回復(fù)

    您需要登錄后才可以回帖 登錄 | 立即注冊

    本版積分規(guī)則


    聯(lián)系客服 關(guān)注微信 下載APP 返回頂部 返回列表