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

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

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

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

[復制鏈接]

552

主題

552

帖子

3302

積分

四級會員

Rank: 4

積分
3302
跳轉(zhuǎn)到指定樓層
樓主
發(fā)表于 2023-12-4 12:00:00 | 只看該作者 |只看大圖 回帖獎勵 |倒序瀏覽 |閱讀模式

7 x  U6 f( Z+ T. M& a點擊上方藍色字體,關(guān)注我們# k" E! f; x6 C1 ~
# D7 `3 y2 p1 r( X/ s: S; x  a
1
( b9 v! `% k. d, J/ B- mvolatile關(guān)鍵字
& }& w& s5 M& |" K3 N' Dvolatile是一個特征修飾符,提醒編譯器它后面所定義的變量隨時都有可能改變,因此編譯后的程序每次需要存儲或讀取這個變量的時候,告訴編譯器對該變量不做優(yōu)化,都會直接從變量內(nèi)存地址中讀取數(shù)據(jù),從而可以提供對特殊地址的穩(wěn)定訪問。
, d/ g$ X- C6 G
) Z) k$ N" S0 o/ ?( H. K常用場景:中斷服務(wù)與主程序共享變量。示例代碼如下:9 w; q$ _3 ?3 x  F3 B( ~3 s5 z
( i- E9 X0 c! Q8 O6 X5 ^7 J/ }
  • //volatile uint8_t flag=1;uint8_t flag=1;9 z5 }0 |0 D; Q( W8 }9 G
    void test(void){    while(flag)    {        //do something    }}
    5 j( j+ H; t1 p6 X//interrupt service routinevoid isr_test(void){    flag=0;}, z0 H  z+ c1 ]' n# N+ P. _
    如果沒使用volatile定義flag,可能在優(yōu)化后test陷入死循環(huán),因為test里使用的flag并沒修改它,開啟優(yōu)化后,編譯器可能會固定從某個內(nèi)存取值。
    * w" L5 h! [1 y  l, r' v4 U; s
    3 X+ w$ f. Z5 H/ @/ O( |1 I9 \2
    6 [8 F1 v6 N% Mconst關(guān)鍵字
    2 S9 K/ L; b& V+ G' Mconst 是 constant 的縮寫,意思是“恒定不變的”,它是定義常變量的關(guān)鍵字。+ ]- ?, z' W; D' s5 h. E
    通常有4種用法。: _( _% h4 t) W

    ' ]& b1 p1 Y. A! R6 b

    7 h" j8 s( w$ U  l5 e3 w4 V" {1、修飾變量  |2 G/ G' h8 L& w6 G3 h0 X% i9 W
    & }9 H9 b3 z+ E% z# F# i' D
    采用const修飾變量,即變量聲明為只讀,保護變量值以防被修改。
    * ~- D" o# J3 @8 x. c& b6 L: o* k4 j1 c
  • const int i = 1;或者int const i=1;
    ; [+ \" I, X0 a, I0 o變量i具有只讀特性,不能夠被更改;若想對i重新賦值,如i = 10,屬于錯誤操作。! E$ B' I3 R! {
    2 U* m4 ^, k' E3 ]( l6 d$ M

    " F% L/ S4 w6 g# m7 S1 U/ g* F7 ^2、修飾數(shù)組
    ; Y& d; ]1 L5 p2 G9 Q) d# h
    ; E5 B! u. u3 l數(shù)組元素與變量類似,具有只讀屬性,不能被更改,一旦更改,編譯時就會報錯。
    ) J, H# ?5 _: m7 Q, y7 U/ L' c* v( q9 ~( w& U6 m) o
  • const int array[5] = {1,2,3,4,5};array[0] = array[0]+1; //錯誤,array是只讀的,禁止修改. V; C1 ?/ t; b
    使用大數(shù)組存儲固定的信息,例如查表(表驅(qū)動法的鍵值表),可以使用const節(jié)省ram。編譯器并不給普通const只讀變量分配空間,而是將它們保存到符號表中,無需讀寫內(nèi)存操作,程序執(zhí)行效率也會提高。
    , T- g3 E3 r5 K) A/ E2 O% z! X

    " T' l6 w/ ]' t) r* H3、修飾指針
    $ ]3 ]$ L3 z# i' ~
    / H- V3 C, F3 [C語言中const修飾指針要特別注意,共有兩種形式,一種是用來限定指向空間的值不能修改;另一種是限定指針不可更改。# ~! b$ b: @" y$ c" J  M0 n
    # K! p3 p+ D" W7 ]* Y) l; \
  • int i = 1;int j = 2;+ m5 i: A" T. V( N# Z4 G3 z
    const int *p1 = &i;int* const p2 = &j;' |' L9 h1 Y2 m5 X- A3 u9 g
    上面定義了兩個指針p1和p2,區(qū)別是const后面是指針本身還是指向的內(nèi)容。4 c1 z# p% E. C/ J
    " `* j# G: C- N4 D# U2 @8 |5 s
    在定義1中const限定的是*p1,即其指向空間的值不可改變,若改變其指向空間的值如*p1=10,則程序會報錯;但p1的值是可以改變的,對p1重新賦值如p1=&k是沒有任何問題的。
    5 [7 |. g! ]- q8 l0 T) J: k1 i5 `6 [  q7 G, _7 i
    在定義2中const限定的是指針p2,若改變p2的值如p2=&k,程序?qū)䦂箦e;但*p2,即其所指向空間的值可以改變,如*p2=20是沒有問題的,程序正常執(zhí)行。6 u  Y- M  H' n" s* p: q/ q

    ! u$ T' p4 M9 i+ g

    * B; V! I+ k; l' s7 \, V4、 修飾函數(shù)參數(shù)' D2 k! }  f% L- V
    const關(guān)鍵字修飾函數(shù)參數(shù),對參數(shù)起限定作用,防止其在函數(shù)內(nèi)部被修改,所限定的函數(shù)參數(shù)可以是普通變量,也可以是指針變量。7 X" m4 o# u8 t
    # `. z2 T6 d7 K' Q" U, E
  • void fun(const int i){    ……    i++; //對i的值進行了修改,程序報錯}
    7 `8 F& n( E7 J6 j: C7 Q2 L! R常用的函數(shù)如strlen。( c. `0 D' [, p& e7 z

    ! m3 y5 n2 a: a. v$ S
  • size_t strlen(const char *string);4 c6 [0 f1 F' ~% X
    const在庫函數(shù)中使用非常普遍,是一種自我保護的安全編碼思維。, e. H$ Y8 m- C9 N
    31 ]" @/ s8 \" E1 _; G' w& w/ }
    static關(guān)鍵字- Y, C1 V5 p( M: U; F1 d3 t" ]) l
    1、static修飾全局變量,該變量只在本文件內(nèi)被訪問,不能在其他文件被直接訪問。
    4 b: {: y; s( N7 {8 h, x: Y. G+ k! C' R# q9 P- P
    2、static修飾函數(shù),該函數(shù)只能在本文件內(nèi)被訪問,不能被其他文件訪問。但是可以通過嵌套的方式調(diào)用,變相的封裝的表現(xiàn)。
    9 @2 U; j2 C2 Q/ D, W# T
    ; E6 g* r8 K0 I  D% _2 l3、static修飾局部變量,更改該局部變量的生命周期。  i# |: k2 k, ]% w
  • 生命周期:將臨時變量的生命周期變成全局變量的生命周期。
  • 作用域不變:作用域仍然是在本代碼塊內(nèi)。
    $ D. k4 Q) [# ]& h5 E5 ~$ {0 `

    / r/ }1 z3 T6 ~! _- Q) y' I" C4
    + ^* c: g! @2 l0 c8 Mstruct與union* c* J5 D/ f3 @
    可以使用struct結(jié)構(gòu)體來存放一組不同類型的數(shù)據(jù)。  G9 u! C; w& H& }8 J2 S

    $ |; b1 i- {; A
  • struct 結(jié)構(gòu)體名{    結(jié)構(gòu)體所包含的變量或數(shù)組};
    " }$ J0 ]2 y; Q- L; [' m結(jié)構(gòu)體是一種集合,它里面包含了多個變量或數(shù)組,它們的類型可以相同,也可以不同,每個這樣的變量或數(shù)組都稱為結(jié)構(gòu)體的成員,通常我們使用結(jié)構(gòu)體定義和解析協(xié)議,如下所示:0 y  S1 u, M" v3 _+ E- I# N6 f

    5 y; R0 o5 ?& E) e& d4 F% a' [' J
  • // 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é)點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:運行模式,1:配置模式,2:節(jié)點升級,3:節(jié)點重啟    uint16_t crc;               // 校驗位}ReceiveData_Mode_t;#pragma pack()8 f+ c% ~3 n; h/ @5 H& M. @3 J% d
    union共用體關(guān)鍵字,定義union下面的成員變量共享一塊內(nèi)存,每一個成員在任一時刻有且只有一個成員使用此塊內(nèi)存。) R& W! d4 F" G, Z1 R
    * v" y; A" K, V
  • union 共用體名{    成員列表};
    1 f0 O0 n2 x& U3 F結(jié)構(gòu)體和共用體的區(qū)別在于:結(jié)構(gòu)體的各個成員會占用不同的內(nèi)存,互相之間沒有影響;而共用體的所有成員占用同一段內(nèi)存,修改一個成員會影響其余所有成員。0 ~9 v+ c- x' A/ [# i/ _
    通常使用共用體做一些標志位操作,例如以下示例,可以非常靈活的訪問Val中的bit位。
    % B, q2 Z) v& s+ f
    0 Q! {8 C+ Y" C. A0 w
  • 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;0 |: |$ U3 z9 n, {9 g  z! _6 ~
    或者使用共用體實現(xiàn)單字節(jié)與多字節(jié)的轉(zhuǎn)化和拼接,如下所示:
    # k1 j6 H# V) L+ G5 x; J8 s. O  O1 w3 @' E2 y& E
  • #include "stdio.h"
    . T: [# n" F# E, h2 v% Ttypedef struct{    union    {        struct        {            unsigned char low;            unsigned char high;        };        unsigned short result;    };}test_t;' M" q& l* }0 x: `1 E# c* a
    int main(int argc, char *argv[]){    test_t hello;
    ' h! B$ `! T8 n8 p. s    hello.high=0x12;    hello.low=0x34;
    . J4 ^1 g, c% Z1 |  ~6 r0 y0 t. u- X3 L    printf("result=%04X\r6 w- P1 ~- ^% [5 V. a' w3 ?
    ",hello.result);//輸出 result=1234 6 y% t) y% Q/ g& p
        return 0;}
    7 x; j! v0 |+ O" Q4 p2 N6 s
    4 O0 k7 x" J" D5
    3 D9 y; {* K' ]2 h- {預定義標識符
    # _' P6 @6 q: X; m% ~7 i" F  q) ~一般編譯器都支持預定義標識符,這些標識符結(jié)合printf等打印信息幫助程序員調(diào)試程序是非常有用的,一般編譯器會自動根據(jù)用戶指定完成替換和處理。
    8 U: |' f( W2 W1 |  }2 ~" Y( n/ X  I9 Q$ I: |) @! P/ p
    常用的預定義標識符如下所示:) }! e( T" ^' v( t( i, m

    5 c; G% N; G" |  N
  • __FILE__    //表示編譯的源文件名__LINE__   //表示當前文件的行號__FUNCTION__  //表示函數(shù)名__DATE__  //表示編譯日期__TIME__   //表示編譯時間1 y6 P% E# k  E4 p7 R; B4 M" F
    在Debug打印日志時候經(jīng)常會用到,如下所示:
      I8 b; w; k6 H* G1 h+ Q; \' U7 ]" b: B5 y& W- n4 u
  • printf("file:%s,line:%d,date:%s,time:%s",__FILE__,__LINE__,__DATE__,__TIME__);
    ) y; I& H! S6 W; ?9 i, `) z' F# {/ U& s9 @0 r( K$ H
    62 `$ ^' j+ _% S+ P- c
    #與##6 q. J" P/ \, p, b$ n
    #:是一種運算符,用于帶參宏的文本替換,將跟在后面的參數(shù)轉(zhuǎn)成一個字符串常量。
    3 Q% F: ^# X+ H6 T- h& R% i. v6 f" x) x
    ##:是一種運算符,是將兩個運算對象連接在一起,也只能出現(xiàn)在帶參宏定義的文本替換中。
    ( J7 }6 Q! {2 X- D  E/ c1 s+ H+ x! v
  • #include "stdio.h"  U" j& @/ M6 F( N! |7 n7 V3 X
    #define TO_STR(s) #s#define COMB(str1,str2) str1##str2* k( O0 q9 j0 n; I- T
    int main(int argc, char *argv[]){    int UART0= 115200;$ W9 P* L5 n* ~0 z, \3 t+ r
        printf("UART0=%d
    . ?% F; E6 y% Q- u) T$ I", COMB(UART, 0));//字符串合并為變量UART0    printf("%s
    3 H) l+ u# _  Z% N. J! `", TO_STR(3.14));//將數(shù)字變成字符串
    % L) |1 R/ j0 u; p; F4 C3 R2 _8 a7 a) B    return 0;}
    - [$ M( O* J! L" v+ d) @
    ' F# q8 S: x& R( E3 G7" W. k2 Q, E2 l& Q  f
    void 與 void*關(guān)鍵字
    + L' M: O& N: i4 Q3 zvoid表示的是無類型,不能聲明變量或常量,但是可以把指針定義為void類型,如void* ptr。void* 指針可以指向任意類型的數(shù)據(jù),在C語言指針操作中,任意類型的數(shù)據(jù)地址都可轉(zhuǎn)為void* 指針。因為指針本質(zhì)上都是unsigned int。
    ! G# i* n8 ?5 o' c5 W. M6 S' S* X2 j# M! b' o6 |( ?
    常用的內(nèi)存塊操作庫函數(shù):0 b$ Z; H! S1 p8 E3 z: N; \# l8 ^. V' x

    - \( @4 `8 {3 Y! d$ t* e6 _& w& @
  • void * memcpy( void *dest, const void *src, size_t len );void * memset( void *buffer, int c, size_t num);
    ' m3 s8 ]- p* l! I, v數(shù)據(jù)指針為void* 類型,對傳入任意類型數(shù)據(jù)的指針都可以操作。另外其中memcpy第二個參數(shù),const現(xiàn)在也如前文所述,拷貝時對傳入的原數(shù)據(jù)內(nèi)容禁止修改。
    7 i  y; u" M6 [+ {7 u% w9 u) P' e9 t  x: e$ K% \- g
    特殊說明,指針是不能使用sizeof求內(nèi)容大小的,在ARM系統(tǒng)固定為int 4字節(jié)。對于函數(shù)無輸入?yún)?shù)的,也盡量加上void,如下所示:
    7 e4 j- ~5 c, m- H7 j. ~. F: w7 \3 Z- J. B
  • void fun(void);
    7 `1 Y+ |5 y8 V  a. G, Q0 g$ `' J$ {" Y. a; N5 y$ ?# I' L
    8
    % J# z5 v- b/ y2 e2 |4 V, f6 Mweak關(guān)鍵字
    ( |& H$ T1 j- k4 N一般簡化定義如下所示:+ s/ _! I$ p' A  i5 E6 ]

    ! `: }$ q: m+ s
  • #define _WEAK __attribute__((weak))
    6 ^- o. a* ]; z2 K函數(shù)名稱前面加上__WEAK屬性修飾符稱為“弱函數(shù)”,類似C++的虛函數(shù)。鏈接時優(yōu)先鏈接為非weak定義的函數(shù),如果找不到則再鏈接帶weak函數(shù)。6 J, k0 Z9 a+ B$ L1 ^8 F4 i0 A/ C
    $ d9 R- E* Q0 M  Z
  • _WEAK void fun(void)  {      //do this}  " [- z9 ^7 w) h' G0 S/ {! w. e
    //不在同一個.c,兩同名函數(shù)不能在同一個文件void fun(void)  {      //do that}3 Y, U7 L  J! U! u
    這種自動選擇的機制,在代碼移植和多模塊配合工作的場景下應(yīng)用較多。例如前期移植代碼,需要調(diào)用某個接口fun,但當前該接口不存在或者未移植完整使用,可以使用weak關(guān)鍵字定義為空函數(shù)先保證編譯正常。
    0 X& \, t/ Z% K% [! j4 C6 D
    3 e+ |0 w& `" |) ]$ n0 i4 ?后續(xù)移植完成實現(xiàn)了fun,即軟件中有2個fun函數(shù)沒有任何錯誤,編譯器自動會識別使用后者。當然也粗暴的#if 0屏蔽對fun的調(diào)用,但要確保后續(xù)記得放開。$ h) B: ]. j6 \5 C

    6 x" t: p7 Z6 J  G% N; w$ C; S
    3 Q! M  [2 H& _& m( D* c2 c往期推薦什么是內(nèi)存碎片?. q8 k% p7 i$ G- Y( l5 W: T
    詳解UDS CAN診斷:什么是UDS(ISO 14229)診斷?
    ; S  I0 [6 `- r$ \  F: U0 e磁耦合共振無線供電裝置
    8 }3 e, }" K5 g- x% k4 w: yC語言:十六進制(HEX)和浮點類型(float、double)轉(zhuǎn)換
    4 w2 x# f! @# D9 _+ ?5 G; KHarmonyOS 分布式多端應(yīng)用一站式開發(fā)平臺(DevEco Studio 安裝); t  a/ D! ]( W6 P% ?7 i
    , h9 I. N! L7 a) {0 C. a. B) G0 b7 y
    & ~. @& s, K0 g$ A. D

    : P3 F; N6 f  N4 H  x1 T& x ( P5 T0 j" O+ k: Q# w
    點擊閱讀原文,更精彩~
  • 回復

    使用道具 舉報

    發(fā)表回復

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

    本版積分規(guī)則


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