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

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

搜索
查看: 16|回復: 0
收起左側

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

[復制鏈接]

563

主題

563

帖子

3385

積分

四級會員

Rank: 4

積分
3385
跳轉(zhuǎn)到指定樓層
樓主
發(fā)表于 2023-12-4 12:00:00 | 只看該作者 |只看大圖 回帖獎勵 |倒序瀏覽 |閱讀模式
$ u) Y6 E/ P2 K2 O- u
點擊上方藍色字體,關注我們$ U. C6 V/ W; `; N! Y: I
6 Y  U$ V/ {* ?3 a; g8 d; M
1- g3 `- t( ]4 V
volatile關鍵字! `0 h/ _( p& D- M" M
volatile是一個特征修飾符,提醒編譯器它后面所定義的變量隨時都有可能改變,因此編譯后的程序每次需要存儲或讀取這個變量的時候,告訴編譯器對該變量不做優(yōu)化,都會直接從變量內(nèi)存地址中讀取數(shù)據(jù),從而可以提供對特殊地址的穩(wěn)定訪問。
# ~1 F! P1 J3 Z& @# G0 q9 G/ r7 h; J! M* H+ U  W: j( f7 h
常用場景:中斷服務與主程序共享變量。示例代碼如下:
* h9 I- L0 K6 n9 \2 H! C4 ^) W- r0 I/ O% x: M1 I* N6 g$ o/ ~4 f
  • //volatile uint8_t flag=1;uint8_t flag=1;
    ( F+ X: B$ B# ]3 |3 Z, D7 y, O( r# _void test(void){    while(flag)    {        //do something    }}0 t* `$ w& R( n/ r. h3 l
    //interrupt service routinevoid isr_test(void){    flag=0;}
    : d$ g% m2 _/ H' h8 r如果沒使用volatile定義flag,可能在優(yōu)化后test陷入死循環(huán),因為test里使用的flag并沒修改它,開啟優(yōu)化后,編譯器可能會固定從某個內(nèi)存取值。
    4 N: L& S" h, e
    " _3 ^  I1 a) G) o. Y. e+ V9 G2; E& e6 c. I- f' V
    const關鍵字; X% e! A6 }& s7 F
    const 是 constant 的縮寫,意思是“恒定不變的”,它是定義常變量的關鍵字。
    ' v( w) n, p. V! D; K; u0 X通常有4種用法。
    ( _* B' y! F2 l. P. w
    ; Q7 J6 g: Q4 E- `# ~1 i
    ; s- L7 X0 T: X+ x3 q
    1、修飾變量3 e* [" n+ L0 X5 K& r6 u2 V1 y$ g
    ! u: _4 c& J( w" M; X1 d9 O: z
    采用const修飾變量,即變量聲明為只讀,保護變量值以防被修改。
    " r; \; R; T" F  n, V
    - _, A6 w/ n4 s: E
  • const int i = 1;或者int const i=1;- @5 [- i! I1 f  m, S* ~
    變量i具有只讀特性,不能夠被更改;若想對i重新賦值,如i = 10,屬于錯誤操作。. b8 {" N+ L4 B" F

    ! @  R3 P; e. @2 {9 T4 L( j
    ! S2 E& U: `% A/ {
    2、修飾數(shù)組: [( g3 Z& e' l8 ?: D/ g+ G

    1 Y' e" V: J, v9 e數(shù)組元素與變量類似,具有只讀屬性,不能被更改,一旦更改,編譯時就會報錯。
    9 @" I8 n6 F. `+ m- K# \( u% {, F$ }1 x+ e" Y( O3 ~
  • const int array[5] = {1,2,3,4,5};array[0] = array[0]+1; //錯誤,array是只讀的,禁止修改: _: g2 D0 k  O6 `2 C2 h' P( C
    使用大數(shù)組存儲固定的信息,例如查表(表驅(qū)動法的鍵值表),可以使用const節(jié)省ram。編譯器并不給普通const只讀變量分配空間,而是將它們保存到符號表中,無需讀寫內(nèi)存操作,程序執(zhí)行效率也會提高。
    7 @5 a- f. P& Y3 O5 p1 _! ~4 c. Q5 a7 i) S& u( W0 y

    ) i- k. G; N7 y  _3、修飾指針' q/ M7 c( l1 g; r6 j* J

    6 @* B$ e% D1 V9 ?- ]! M! t9 v/ sC語言中const修飾指針要特別注意,共有兩種形式,一種是用來限定指向空間的值不能修改;另一種是限定指針不可更改。! {8 d. l3 C( d' ]& W" D; K: i
    6 {8 d8 a/ A+ K4 S
  • int i = 1;int j = 2;- G3 [' C5 o0 j6 A
    const int *p1 = &i;int* const p2 = &j;. K9 j. T$ n* a; f  ?
    上面定義了兩個指針p1和p2,區(qū)別是const后面是指針本身還是指向的內(nèi)容。6 L" T( b/ Q2 C% L' ~. P: I. A
    1 {+ M8 ]! ]4 f, Q# T8 Y6 q
    在定義1中const限定的是*p1,即其指向空間的值不可改變,若改變其指向空間的值如*p1=10,則程序會報錯;但p1的值是可以改變的,對p1重新賦值如p1=&k是沒有任何問題的。
    . O* C8 J% v/ I8 S4 }" y- `
    , B. u# d' n4 W在定義2中const限定的是指針p2,若改變p2的值如p2=&k,程序?qū)䦂箦e;但*p2,即其所指向空間的值可以改變,如*p2=20是沒有問題的,程序正常執(zhí)行。
    6 C) m7 _. R5 z( l
    0 E6 H: ~/ c3 n, p* T( ^' v
    ) _! H& K5 v, f0 v- R7 [" H
    4、 修飾函數(shù)參數(shù)$ q! p2 d/ p) n3 k: ?
    const關鍵字修飾函數(shù)參數(shù),對參數(shù)起限定作用,防止其在函數(shù)內(nèi)部被修改,所限定的函數(shù)參數(shù)可以是普通變量,也可以是指針變量。& u$ u& x8 N! `/ u: h% V$ j6 a

    ' C) p1 v4 G& B5 X; r7 }
  • void fun(const int i){    ……    i++; //對i的值進行了修改,程序報錯}+ Y9 M! t  [" J- l( m
    常用的函數(shù)如strlen。/ Q* B8 T0 ?. K" ?& E
    & }  K" U. e. m
  • size_t strlen(const char *string);
    ) O" x3 `8 s  ?2 J* Uconst在庫函數(shù)中使用非常普遍,是一種自我保護的安全編碼思維。
    3 l& ^  `* c  ^; |1 K3 T- |3
    # Q* x9 y' s+ ostatic關鍵字
    5 R. U) o- E" q5 T- B2 S0 p1、static修飾全局變量,該變量只在本文件內(nèi)被訪問,不能在其他文件被直接訪問。
    & r6 d/ |* e; K+ s3 {' J' h/ t8 B% K0 B8 N6 Q+ Y+ B( K
    2、static修飾函數(shù),該函數(shù)只能在本文件內(nèi)被訪問,不能被其他文件訪問。但是可以通過嵌套的方式調(diào)用,變相的封裝的表現(xiàn)。3 w7 w% k0 y, Y* E( m1 h) A

    9 I, u: }- U4 E; |' q3、static修飾局部變量,更改該局部變量的生命周期。& y& M8 e) V7 \% u5 S, o
  • 生命周期:將臨時變量的生命周期變成全局變量的生命周期。
  • 作用域不變:作用域仍然是在本代碼塊內(nèi)。) A7 }! [3 [- ]; M. L) Z! \- S
    8 I% g/ x$ f* O; K/ `/ g, \
    4! J" f" N! ~) j3 D. F# y
    struct與union
    : C0 c- t: X4 @. i6 {0 c) V! O$ B9 X可以使用struct結構體來存放一組不同類型的數(shù)據(jù)。
    + d6 t2 ^3 i9 w
    $ @) T6 v6 S. K! E- ?$ O
  • struct 結構體名{    結構體所包含的變量或數(shù)組};
    # X: Q# j" E0 K& e) e2 i$ Y. f3 n結構體是一種集合,它里面包含了多個變量或數(shù)組,它們的類型可以相同,也可以不同,每個這樣的變量或數(shù)組都稱為結構體的成員,通常我們使用結構體定義和解析協(xié)議,如下所示:
    & \# t  Q; _8 W6 ?* B; }0 W6 @- J7 C$ Z2 H0 ]
  • // 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()3 p8 L, ^! z( ]8 r3 D# ~/ _8 T: @, ]
    union共用體關鍵字,定義union下面的成員變量共享一塊內(nèi)存,每一個成員在任一時刻有且只有一個成員使用此塊內(nèi)存。" D5 `/ P/ I4 i* {
    4 t0 K7 k  K6 y2 ~7 ^
  • union 共用體名{    成員列表};
    7 f+ Z; f* J( ]結構體和共用體的區(qū)別在于:結構體的各個成員會占用不同的內(nèi)存,互相之間沒有影響;而共用體的所有成員占用同一段內(nèi)存,修改一個成員會影響其余所有成員。
    ' P  q0 }8 y3 y' R2 I通常使用共用體做一些標志位操作,例如以下示例,可以非常靈活的訪問Val中的bit位。) g! Z9 H- v+ N3 H3 M; `# A: e- B

    + F3 `1 H3 N. R1 Q1 I
  • 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;
    ) a% |! I! ^8 R9 U* B) N或者使用共用體實現(xiàn)單字節(jié)與多字節(jié)的轉(zhuǎn)化和拼接,如下所示:
    6 E8 r6 P. s& O3 X9 u) C4 w9 J2 D. C7 a# X/ M. s
  • #include "stdio.h"  Z. i9 Z4 u  h/ ~
    typedef struct{    union    {        struct        {            unsigned char low;            unsigned char high;        };        unsigned short result;    };}test_t;
    + R$ A( ~5 B4 @int main(int argc, char *argv[]){    test_t hello;
    7 x4 l! I# O! d    hello.high=0x12;    hello.low=0x34;
    , w& |' X4 |& P  u& g    printf("result=%04X\r
      F9 K4 T3 k# |5 R' N8 S$ J) q) S",hello.result);//輸出 result=1234 & ^5 ?! `8 P/ I  g9 E
        return 0;}
    4 C  M0 |8 y6 ?/ ]- s( F! c6 S' t) A% m7 [* S7 w
    5
    7 w% q) R: b' A; {6 ~預定義標識符
    4 L4 ~8 y3 t% C( Q: q" H4 }一般編譯器都支持預定義標識符,這些標識符結合printf等打印信息幫助程序員調(diào)試程序是非常有用的,一般編譯器會自動根據(jù)用戶指定完成替換和處理。6 R3 I' o: b7 L5 b
    4 v; _/ E3 z; L
    常用的預定義標識符如下所示:- }& r4 U! T7 t% F8 m
    * M3 \* R% |7 n) F) d# S
  • __FILE__    //表示編譯的源文件名__LINE__   //表示當前文件的行號__FUNCTION__  //表示函數(shù)名__DATE__  //表示編譯日期__TIME__   //表示編譯時間2 `  D! ^3 h: h5 L9 w
    在Debug打印日志時候經(jīng)常會用到,如下所示:
    7 b! \6 u0 b& r$ h# G+ U2 D" M+ Y, V: \3 H
  • printf("file:%s,line:%d,date:%s,time:%s",__FILE__,__LINE__,__DATE__,__TIME__);/ T/ @: Z9 E, f& e

      a1 k, V2 y/ D( E5 x- r* V' V6, H: p; b# b* m2 L1 m) |
    #與##3 r5 I: x' f! u
    #:是一種運算符,用于帶參宏的文本替換,將跟在后面的參數(shù)轉(zhuǎn)成一個字符串常量。' b# Z3 b8 I  k4 l7 `# b
    6 v4 s: k( K0 o0 V" l# H9 C- h( E
    ##:是一種運算符,是將兩個運算對象連接在一起,也只能出現(xiàn)在帶參宏定義的文本替換中。3 ^  W6 f2 f$ ~9 t; G

    ) i5 ]$ @5 x5 \$ n& D
  • #include "stdio.h"; U/ U9 a2 A. g, a; k2 Y4 f0 k
    #define TO_STR(s) #s#define COMB(str1,str2) str1##str2  x  t% ?) @) s
    int main(int argc, char *argv[]){    int UART0= 115200;6 c! M# `7 W& g6 L
        printf("UART0=%d
      E4 {* k; K4 M' w  c$ n", COMB(UART, 0));//字符串合并為變量UART0    printf("%s
    - h: z7 J) x& s* {9 S3 L", TO_STR(3.14));//將數(shù)字變成字符串
    + t$ ]9 b+ ^: c+ n& ~: G4 W, u    return 0;}
    $ [$ u% e" W# g# l, u/ j  w, h$ A. r2 v  N; P9 Q# K" ?. X2 M
    7
    - I! t# \/ [& pvoid 與 void*關鍵字
    % S  }' s  z9 tvoid表示的是無類型,不能聲明變量或常量,但是可以把指針定義為void類型,如void* ptr。void* 指針可以指向任意類型的數(shù)據(jù),在C語言指針操作中,任意類型的數(shù)據(jù)地址都可轉(zhuǎn)為void* 指針。因為指針本質(zhì)上都是unsigned int。
    7 h$ q0 ~0 e% o5 b" @4 _
    ( Y* H. f; h( |7 u- e常用的內(nèi)存塊操作庫函數(shù):7 J/ I% h- H. K* ?8 [* p' D; i1 M
    6 q8 g# G& M8 Z) j
  • void * memcpy( void *dest, const void *src, size_t len );void * memset( void *buffer, int c, size_t num);
    & n* b6 O9 J; S) ?, M數(shù)據(jù)指針為void* 類型,對傳入任意類型數(shù)據(jù)的指針都可以操作。另外其中memcpy第二個參數(shù),const現(xiàn)在也如前文所述,拷貝時對傳入的原數(shù)據(jù)內(nèi)容禁止修改。) {) v$ Y7 y. V8 F6 w8 l7 |
    * a& R7 Q, l/ \# P3 w! _3 V
    特殊說明,指針是不能使用sizeof求內(nèi)容大小的,在ARM系統(tǒng)固定為int 4字節(jié)。對于函數(shù)無輸入?yún)?shù)的,也盡量加上void,如下所示:/ k: {9 ~6 p3 ~2 r" V3 ?6 t& i3 ~
      f* b; m8 Z  v6 N( B% ]8 }
  • void fun(void);
    9 ^) _& o; d1 g- |. _5 l1 @  B$ D8 b9 l, `' ?  [  |
    8$ a& w+ u6 \9 G& d1 O; n# A
    weak關鍵字
    : ?: \' V% N: Y5 C  Q$ ~一般簡化定義如下所示:
    : Q1 I) A* ?7 o5 d7 {
    1 L3 b+ x7 Z3 C, N! z
  • #define _WEAK __attribute__((weak))" ]& ~' G# |; c0 C
    函數(shù)名稱前面加上__WEAK屬性修飾符稱為“弱函數(shù)”,類似C++的虛函數(shù)。鏈接時優(yōu)先鏈接為非weak定義的函數(shù),如果找不到則再鏈接帶weak函數(shù)。
    0 J+ t+ P7 d3 e7 e  l9 p9 o' [% r' t% @! M5 T
  • _WEAK void fun(void)  {      //do this}  
    ) F9 z# t; L( }: t//不在同一個.c,兩同名函數(shù)不能在同一個文件void fun(void)  {      //do that}5 t/ T4 t9 g! |+ m
    這種自動選擇的機制,在代碼移植和多模塊配合工作的場景下應用較多。例如前期移植代碼,需要調(diào)用某個接口fun,但當前該接口不存在或者未移植完整使用,可以使用weak關鍵字定義為空函數(shù)先保證編譯正常。
    % h0 R# S7 }, z# K. X. z
    8 |2 f( ~, Z% [* v后續(xù)移植完成實現(xiàn)了fun,即軟件中有2個fun函數(shù)沒有任何錯誤,編譯器自動會識別使用后者。當然也粗暴的#if 0屏蔽對fun的調(diào)用,但要確保后續(xù)記得放開。
      v1 ]8 R( ]: m$ ]3 _
    3 e' }# a! G) `2 B+ E; k' l* Y 8 t- x# c' p& \5 G  ~; }) X5 i' G
    往期推薦什么是內(nèi)存碎片?- ?- U3 y# j7 X$ P7 `& d9 F- @! J
    詳解UDS CAN診斷:什么是UDS(ISO 14229)診斷?
    3 F, z3 X! E& X- g/ x2 i磁耦合共振無線供電裝置5 C# E  x, F& r; }1 u# G# F
    C語言:十六進制(HEX)和浮點類型(float、double)轉(zhuǎn)換
      N" s' _% C2 ], c, L3 xHarmonyOS 分布式多端應用一站式開發(fā)平臺(DevEco Studio 安裝)* ^; Y( N  F* f

    ' N: _; Q& W) n+ v6 Q+ m. c

    * g: G1 K- ^( i# S6 j( }% z
    * W; V/ t# n: D& i
    5 g, j1 o/ k, n3 A+ w! \2 Q- k. k點擊閱讀原文,更精彩~
  • 回復

    使用道具 舉報

    發(fā)表回復

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

    本版積分規(guī)則


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