|
pqjd2o1bjtm64011336636.gif (60.41 KB, 下載次數(shù): 0)
下載附件
保存到相冊
pqjd2o1bjtm64011336636.gif
2024-9-9 09:51 上傳
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 H7 |; ^; 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/ Fint 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* Lstruct 結(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! _ runion 共用體名{ 成員列表};& 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. stypedef 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 {' ^% tvoid 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
4az1cr4tckv64011336736.png (401 Bytes, 下載次數(shù): 0)
下載附件
保存到相冊
4az1cr4tckv64011336736.png
2024-9-9 09:51 上傳
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
vrrnfc2hjre64011336836.jpg (71.14 KB, 下載次數(shù): 0)
下載附件
保存到相冊
vrrnfc2hjre64011336836.jpg
2024-9-9 09:51 上傳
: ]& l9 v6 t8 k" w+ C3 V s2 h
fqzxfmocn5o64011336936.gif (45.46 KB, 下載次數(shù): 0)
下載附件
保存到相冊
fqzxfmocn5o64011336936.gif
2024-9-9 09:51 上傳
% N4 X7 w# N% f2 c; q# _! Y點擊閱讀原文,更精彩~ |
|