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

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

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

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

[復(fù)制鏈接]

552

主題

552

帖子

3302

積分

四級會員

Rank: 4

積分
3302
跳轉(zhuǎn)到指定樓層
樓主
發(fā)表于 2023-12-4 12:00:00 | 只看該作者 |只看大圖 回帖獎勵 |倒序瀏覽 |閱讀模式
  Q8 p3 i8 E9 _# S6 O  j
點擊上方藍色字體,關(guān)注我們/ r# E& X# ^. ?* J' y' r: t
8 z+ I) F, H) a7 [, f' y. X
19 b) ~: ^! i  Q9 M" `) c2 }8 n3 b
volatile關(guān)鍵字
4 [: W2 t; c" [! N. ]6 x: ivolatile是一個特征修飾符,提醒編譯器它后面所定義的變量隨時都有可能改變,因此編譯后的程序每次需要存儲或讀取這個變量的時候,告訴編譯器對該變量不做優(yōu)化,都會直接從變量內(nèi)存地址中讀取數(shù)據(jù),從而可以提供對特殊地址的穩(wěn)定訪問。
0 T+ W. @* C( q& f4 P! `
; f0 ^+ {: E& v" o1 \) b常用場景:中斷服務(wù)與主程序共享變量。示例代碼如下:  [$ J! E" T% [- W% o% e

( q6 D6 ^! y4 [3 ~+ H
  • //volatile uint8_t flag=1;uint8_t flag=1;4 i2 r+ c1 q  d1 R. H
    void test(void){    while(flag)    {        //do something    }}6 G& E0 o1 V$ s. e
    //interrupt service routinevoid isr_test(void){    flag=0;}
    ' T- ]9 L- P9 g如果沒使用volatile定義flag,可能在優(yōu)化后test陷入死循環(huán),因為test里使用的flag并沒修改它,開啟優(yōu)化后,編譯器可能會固定從某個內(nèi)存取值。
    . a" y, r- S( o4 m* p& E* K* D
    ! E1 v( @/ q. y3 k4 ^2
    * [  o' C# \! econst關(guān)鍵字& U% k3 x) j3 F" y: e1 |' O
    const 是 constant 的縮寫,意思是“恒定不變的”,它是定義常變量的關(guān)鍵字。9 M& B% A1 q+ q' x0 }* \, z# q
    通常有4種用法。) ^9 ?- _6 K& |1 s/ k1 L# [! L1 r

    + o( f* I5 }  T2 ~4 d, u! y0 r
    % H  V) b9 o: Z: V( v
    1、修飾變量. d9 B" L6 h) W
    ) F$ |8 S: d+ q+ i
    采用const修飾變量,即變量聲明為只讀,保護變量值以防被修改。1 C8 M' x4 u/ K+ T* I  v. g
    - j* s' ~$ |% L
  • const int i = 1;或者int const i=1;
    ' G, N, G9 N, `9 s/ G& f變量i具有只讀特性,不能夠被更改;若想對i重新賦值,如i = 10,屬于錯誤操作。
    " J9 A/ o# ^: ^7 J) L3 q0 r
    : c, L! ]  [6 H
    7 |; ^; N" i: M  P' R
    2、修飾數(shù)組+ L1 o+ @( R# B% p3 J* d) W2 t

    5 F& C) t8 _" A2 R4 m# R4 M數(shù)組元素與變量類似,具有只讀屬性,不能被更改,一旦更改,編譯時就會報錯。. _# U* `) V8 b  Z9 y5 K

    3 W1 K0 J3 d! }% I! {: v& L* ~
  • const int array[5] = {1,2,3,4,5};array[0] = array[0]+1; //錯誤,array是只讀的,禁止修改
    ) R0 h6 S% {3 M& P* w6 O  B使用大數(shù)組存儲固定的信息,例如查表(表驅(qū)動法的鍵值表),可以使用const節(jié)省ram。編譯器并不給普通const只讀變量分配空間,而是將它們保存到符號表中,無需讀寫內(nèi)存操作,程序執(zhí)行效率也會提高。+ d) v6 b$ ^1 k0 C4 p0 l- Z

    3 p* [5 `; N$ C% W" h

    - t! ]4 n0 I, e" p6 V3、修飾指針% i0 ?4 `6 p# G7 \! e) U- |8 I
    # M* U1 c7 t+ ]6 N1 v$ c
    C語言中const修飾指針要特別注意,共有兩種形式,一種是用來限定指向空間的值不能修改;另一種是限定指針不可更改。
    / Q: B' E) E& {5 I
    : T1 c: w7 u% d' P' E/ F
  • int i = 1;int j = 2;
    ) C7 I! ]& U1 D. I. W3 O5 }const int *p1 = &i;int* const p2 = &j;; h. s9 C8 }  D6 R% v% b
    上面定義了兩個指針p1和p2,區(qū)別是const后面是指針本身還是指向的內(nèi)容。
    2 ~7 s2 \, K! A) F
    # J- Y, g: j" g$ M8 ^在定義1中const限定的是*p1,即其指向空間的值不可改變,若改變其指向空間的值如*p1=10,則程序會報錯;但p1的值是可以改變的,對p1重新賦值如p1=&k是沒有任何問題的。
    : }- z# m1 f* U9 f- G- r! M# R7 N4 G
    在定義2中const限定的是指針p2,若改變p2的值如p2=&k,程序?qū)䦂箦e;但*p2,即其所指向空間的值可以改變,如*p2=20是沒有問題的,程序正常執(zhí)行。- P% ~  x5 `$ y$ b5 T% [2 c# [& w- T

    . E4 V" U+ h  C# W! w4 k
    + ?) i" F% Q/ k7 q" f- y
    4、 修飾函數(shù)參數(shù)0 e' K! @. `9 e0 ?2 B
    const關(guān)鍵字修飾函數(shù)參數(shù),對參數(shù)起限定作用,防止其在函數(shù)內(nèi)部被修改,所限定的函數(shù)參數(shù)可以是普通變量,也可以是指針變量。5 {' m7 b! b9 n' x4 T8 _0 `
    $ ?8 Y+ p$ I' C( l  s6 u! I) S
  • void fun(const int i){    ……    i++; //對i的值進行了修改,程序報錯}, N" U! h8 c( J
    常用的函數(shù)如strlen。
    7 w6 y, @0 y/ ~; n; q% P; F( V: ?
    2 i: r' G4 T4 y% C8 \8 n) }
  • size_t strlen(const char *string);1 u( X2 ^  t6 L' g2 a7 I
    const在庫函數(shù)中使用非常普遍,是一種自我保護的安全編碼思維。# \) N% }+ r, j- I% Y4 J
    3
    $ D, K# C* {+ R5 V. c1 lstatic關(guān)鍵字1 f- R% _" s3 V; }
    1、static修飾全局變量,該變量只在本文件內(nèi)被訪問,不能在其他文件被直接訪問。
    . e+ S( ~! s2 R( U( I; |/ ?
    $ m' @, X* v5 E/ N/ `6 N2、static修飾函數(shù),該函數(shù)只能在本文件內(nèi)被訪問,不能被其他文件訪問。但是可以通過嵌套的方式調(diào)用,變相的封裝的表現(xiàn)。7 J# q, O; Q" B- S  V1 b
    8 m# d9 {+ w6 z1 k  _5 x
    3、static修飾局部變量,更改該局部變量的生命周期。
    $ V6 s' n8 f" }# T# }, `% s
  • 生命周期:將臨時變量的生命周期變成全局變量的生命周期。
  • 作用域不變:作用域仍然是在本代碼塊內(nèi)。. x- _+ |1 M  ?/ e. N5 d+ h
      }* D, `$ ?! O0 S# C
    4( T0 M  \; w; ~% R, r3 G  k
    struct與union+ ^) F1 w5 k8 P6 p3 S2 }3 K( _1 ]
    可以使用struct結(jié)構(gòu)體來存放一組不同類型的數(shù)據(jù)。* w1 z9 t, X1 \6 U6 i

    & q# A: v' x* L
  • struct 結(jié)構(gòu)體名{    結(jié)構(gòu)體所包含的變量或數(shù)組};
    + E- T  A9 v. |" W8 d. H/ y結(jié)構(gòu)體是一種集合,它里面包含了多個變量或數(shù)組,它們的類型可以相同,也可以不同,每個這樣的變量或數(shù)組都稱為結(jié)構(gòu)體的成員,通常我們使用結(jié)構(gòu)體定義和解析協(xié)議,如下所示:; w' P0 t+ n5 L# l4 B: Y/ G/ c
    1 G4 v# Y+ L) P' u) t
  • // 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()
    , z+ E% M: I: Zunion共用體關(guān)鍵字,定義union下面的成員變量共享一塊內(nèi)存,每一個成員在任一時刻有且只有一個成員使用此塊內(nèi)存。
    0 G8 L' u( K  m5 K% y: E( B
    1 a4 w$ V8 [( M) R7 i! _  r
  • union 共用體名{    成員列表};& W/ @2 s% G- k: ~7 J& E
    結(jié)構(gòu)體和共用體的區(qū)別在于:結(jié)構(gòu)體的各個成員會占用不同的內(nèi)存,互相之間沒有影響;而共用體的所有成員占用同一段內(nèi)存,修改一個成員會影響其余所有成員。
    4 p3 ?3 q. |/ g) a; I通常使用共用體做一些標(biāo)志位操作,例如以下示例,可以非常靈活的訪問Val中的bit位。
    3 D1 q$ C+ r0 F, U
    + L3 b% g9 e: P, g+ m3 M. s
  • 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;. h; q" E9 \; a
    或者使用共用體實現(xiàn)單字節(jié)與多字節(jié)的轉(zhuǎn)化和拼接,如下所示:4 v0 b, b$ S: Y$ T2 z
    ' V. W( P; X. Y2 m0 ~" T; X  a- K" Q
  • #include "stdio.h"
    - x& J# B+ F1 M7 |  t) ftypedef struct{    union    {        struct        {            unsigned char low;            unsigned char high;        };        unsigned short result;    };}test_t;
    ) `2 _& [6 ?( H5 s/ U% z2 sint main(int argc, char *argv[]){    test_t hello;
    ' e9 U6 z% {  Z; O    hello.high=0x12;    hello.low=0x34;, o& w: H/ F. a, w
        printf("result=%04X\r3 p  I" r" t' ~( E) r  r1 o' x
    ",hello.result);//輸出 result=1234
    1 K9 Y2 _7 O! I    return 0;}
    1 y( a  m) D. k; V; T! _
    5 }* Y) ]0 M1 K( Y8 x8 ^51 v- f% ~5 [& B: s' f
    預(yù)定義標(biāo)識符
    # x+ j' u! U8 g; d+ t/ b4 k4 U4 o一般編譯器都支持預(yù)定義標(biāo)識符,這些標(biāo)識符結(jié)合printf等打印信息幫助程序員調(diào)試程序是非常有用的,一般編譯器會自動根據(jù)用戶指定完成替換和處理。* K2 K- W; W- j( s3 z

    * J' x* y1 J7 Y) E1 i常用的預(yù)定義標(biāo)識符如下所示:% i8 M( e1 Y! A; J6 Q( y
    + i& i! N  _. G. t! Z
  • __FILE__    //表示編譯的源文件名__LINE__   //表示當(dāng)前文件的行號__FUNCTION__  //表示函數(shù)名__DATE__  //表示編譯日期__TIME__   //表示編譯時間
    ) s2 U3 j' C9 S9 v, f在Debug打印日志時候經(jīng)常會用到,如下所示:
    . J! P+ V$ z- U( y$ q* H3 b7 F$ h; N8 J1 w
  • printf("file:%s,line:%d,date:%s,time:%s",__FILE__,__LINE__,__DATE__,__TIME__);
    4 ^0 d- [2 X9 H) L" P" ]
    / ]3 @" \6 ^$ ?+ M6
    5 k! w* l+ j' c' V/ D: n8 x#與##9 `$ L. Z2 c* a  Y
    #:是一種運算符,用于帶參宏的文本替換,將跟在后面的參數(shù)轉(zhuǎn)成一個字符串常量。
    % K, ]* z% Z4 E( V' ]- Q" N- X4 W- S9 X  W) V
    ##:是一種運算符,是將兩個運算對象連接在一起,也只能出現(xiàn)在帶參宏定義的文本替換中。8 m6 i, a. V/ }* {7 U2 v" h

    1 r0 u! @: q* U6 G# y* v, R" b) i
  • #include "stdio.h"
    ' `# S! w( z2 y#define TO_STR(s) #s#define COMB(str1,str2) str1##str26 C+ I& J& l; Y, s+ o: j! C
    int main(int argc, char *argv[]){    int UART0= 115200;" e+ l8 j- ?& T/ C5 d
        printf("UART0=%d& \! z0 K! R! X$ V
    ", COMB(UART, 0));//字符串合并為變量UART0    printf("%s
    ( s' e$ f/ I6 K& G" [! ^7 z: d", TO_STR(3.14));//將數(shù)字變成字符串" l/ n) }; [( d+ B+ A! W# f* Y1 `
        return 0;}
    . y1 k  Y8 g" U2 }+ R
    . \  r( x" t. r) n7 k4 X7
    7 L! T- {* b, W" R7 w% X- A* _( Lvoid 與 void*關(guān)鍵字9 p+ x+ r* o, D; z  ?
    void表示的是無類型,不能聲明變量或常量,但是可以把指針定義為void類型,如void* ptr。void* 指針可以指向任意類型的數(shù)據(jù),在C語言指針操作中,任意類型的數(shù)據(jù)地址都可轉(zhuǎn)為void* 指針。因為指針本質(zhì)上都是unsigned int。
    5 d  ^4 _( F1 y5 y4 ]: X
    - f$ s5 ?# U8 G6 w常用的內(nèi)存塊操作庫函數(shù):
    * e3 n8 L& n0 T3 a$ U/ b
    + T) f1 M/ w! y( _
  • void * memcpy( void *dest, const void *src, size_t len );void * memset( void *buffer, int c, size_t num);
    / f# w4 K/ T2 }數(shù)據(jù)指針為void* 類型,對傳入任意類型數(shù)據(jù)的指針都可以操作。另外其中memcpy第二個參數(shù),const現(xiàn)在也如前文所述,拷貝時對傳入的原數(shù)據(jù)內(nèi)容禁止修改。
    $ M, J' y9 k0 X! T1 h
    ) B1 t7 ?* ~( n- O特殊說明,指針是不能使用sizeof求內(nèi)容大小的,在ARM系統(tǒng)固定為int 4字節(jié)。對于函數(shù)無輸入?yún)?shù)的,也盡量加上void,如下所示:
    - S; z) \, P+ Z8 K: O
    9 j4 w9 X6 {' ^% t
  • void fun(void);$ N2 F% @: r- V, S8 M6 b

    ! T/ _4 l) o# h. m8, U8 E7 G" h) u8 ?3 {
    weak關(guān)鍵字
    2 v) h( }2 [4 A, T8 ]: W一般簡化定義如下所示:
    0 `1 _: Q) K6 i8 v
    7 ~. t  _& I  N- O( D
  • #define _WEAK __attribute__((weak)). V  C& m& u( x" c5 B
    函數(shù)名稱前面加上__WEAK屬性修飾符稱為“弱函數(shù)”,類似C++的虛函數(shù)。鏈接時優(yōu)先鏈接為非weak定義的函數(shù),如果找不到則再鏈接帶weak函數(shù)。/ e9 V/ z; }: z# O. c, ?

    : O5 H: b. n) N  R$ v
  • _WEAK void fun(void)  {      //do this}  
    9 J8 K3 y: d, q; I% R//不在同一個.c,兩同名函數(shù)不能在同一個文件void fun(void)  {      //do that}
    2 n# D; Q4 S- f1 T6 K這種自動選擇的機制,在代碼移植和多模塊配合工作的場景下應(yīng)用較多。例如前期移植代碼,需要調(diào)用某個接口fun,但當(dāng)前該接口不存在或者未移植完整使用,可以使用weak關(guān)鍵字定義為空函數(shù)先保證編譯正常。
    ( y: P0 }9 d/ S& L' u0 Z5 `
    % v  w8 w  K. E. Q' D& y后續(xù)移植完成實現(xiàn)了fun,即軟件中有2個fun函數(shù)沒有任何錯誤,編譯器自動會識別使用后者。當(dāng)然也粗暴的#if 0屏蔽對fun的調(diào)用,但要確保后續(xù)記得放開。
    % t& X& B( T+ I; g3 W! r# @: N0 [  F' P& |% a/ ]8 s, f
    9 g: X0 ~6 s7 x+ Y7 x% R
    往期推薦什么是內(nèi)存碎片?
    / _" H; f9 }3 L! N, E  d詳解UDS CAN診斷:什么是UDS(ISO 14229)診斷?: s2 N5 J( H6 `/ m
    磁耦合共振無線供電裝置/ P& c$ Y2 m5 O5 X  Q- G
    C語言:十六進制(HEX)和浮點類型(float、double)轉(zhuǎn)換- f$ r" B8 a. g# i
    HarmonyOS 分布式多端應(yīng)用一站式開發(fā)平臺(DevEco Studio 安裝)
    ; Y1 |+ F! ~1 L( G# x6 k: K; x0 d- |3 O& U, _% ]
    # {# m! K) ^: @' \1 b6 ]* H

    : ]& l9 v6 t8 k" w+ C3 V  s2 h
    % N4 X7 w# N% f2 c; q# _! Y點擊閱讀原文,更精彩~
  • 回復(fù)

    使用道具 舉報

    發(fā)表回復(fù)

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

    本版積分規(guī)則


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