|
skk2rppqhah64024997041.gif (60.41 KB, 下載次數(shù): 0)
下載附件
保存到相冊
skk2rppqhah64024997041.gif
2024-9-12 09:50 上傳
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& F4 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/ Xint 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; Fvoid 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 Astruct 結(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% `! Uvoid * 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
z3iwwlzuh1q64024997141.png (401 Bytes, 下載次數(shù): 0)
下載附件
保存到相冊
z3iwwlzuh1q64024997141.png
2024-9-12 09:50 上傳
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
fuk2mic3ad164024997241.jpg (71.14 KB, 下載次數(shù): 0)
下載附件
保存到相冊
fuk2mic3ad164024997241.jpg
2024-9-12 09:50 上傳
& e. _" _5 B* {& p9 _- D6 D- F
rx0bwuyifqw64024997341.gif (45.46 KB, 下載次數(shù): 0)
下載附件
保存到相冊
rx0bwuyifqw64024997341.gif
2024-9-12 09:50 上傳
2 f8 h5 d: \# A8 i點(diǎn)擊閱讀原文,更精彩~ |
|