|

0vgbbq0aug264018782742.gif
3 V6 c7 P& S( ~" o' k; w点击上方蓝色字体,关注我们
$ z" v9 [# J& W# x: Y
" F& t* c: S8 \9 Y12 L8 a" s X, e/ ?
volatile关键字
( ?$ J3 S* K- x2 evolatile是一个特征修饰符,提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,告诉编译器对该变量不做优化,都会直接从变量内存地址中读取数据,从而可以提供对特殊地址的稳定访问。
; Q/ h- h8 D. }9 s7 v9 O& h4 i. y' v* x3 k+ ]2 j) B& u
常用场景:中断服务与主程序共享变量。示例代码如下:
z5 N# S! f8 z/ U9 B& j
! l F# F5 Y6 v3 F3 b6 o7 d( o5 b+ Z5 I//volatile uint8_t flag=1;uint8_t flag=1;; L V5 j1 @- \) m& J
void test(void){ while(flag) { //do something }}
* [+ H1 k) Q6 K' [( K( D3 L//interrupt service routinevoid isr_test(void){ flag=0;}# i5 D+ U+ [& C6 D( k2 C
如果没使用volatile定义flag,可能在优化后test陷入死循环,因为test里使用的flag并没修改它,开启优化后,编译器可能会固定从某个内存取值。
5 k" _+ {# ]' w/ n6 o" Z, z; R* g/ y4 q' `2 n% x
2
1 z+ y7 b5 C" R; W; Kconst关键字, c) L+ I2 x& n M7 U
const 是 constant 的缩写,意思是“恒定不变的”,它是定义常变量的关键字。1 g0 o$ m3 I/ ?; H! g, R; H6 h
通常有4种用法。( s( i* k, ?: l/ j: N; i' T' a
1 b3 Z) n8 V. d% ^7 Q; h
+ a+ t4 x4 {7 d. V K' `2 U5 e1、修饰变量
5 I4 R6 u! U, V
" [, r( Q- I3 c6 M采用const修饰变量,即变量声明为只读,保护变量值以防被修改。
2 O( P. g1 ]/ F5 n) q$ m8 ^. @: r" g9 X- X
const int i = 1;或者int const i=1;) d% _# a/ w n+ P$ h# y
变量i具有只读特性,不能够被更改;若想对i重新赋值,如i = 10,属于错误操作。0 e- e8 F: ?3 o
. [& g+ k* X; X4 l+ H8 u' z$ C( W) D/ Z* H$ t& g3 o
2、修饰数组; P; k {1 K6 h3 g
+ d( z2 r" d4 N数组元素与变量类似,具有只读属性,不能被更改,一旦更改,编译时就会报错。- P2 V+ U7 @' T5 H8 Z8 P# ?
5 s/ C+ [: B* o. f0 v# i1 s+ }const int array[5] = {1,2,3,4,5};array[0] = array[0]+1; //错误,array是只读的,禁止修改
8 \- l7 v3 ~$ x! K使用大数组存储固定的信息,例如查表(表驱动法的键值表),可以使用const节省ram。编译器并不给普通const只读变量分配空间,而是将它们保存到符号表中,无需读写内存操作,程序执行效率也会提高。7 j. s) Y! }* n X% Y: N1 T
9 ~6 c! I9 t- O x ^. H e
& m3 G/ h; Y% k9 i
3、修饰指针
" v$ M, x8 Z+ f- J
, k" M5 m, M: Q" c" EC语言中const修饰指针要特别注意,共有两种形式,一种是用来限定指向空间的值不能修改;另一种是限定指针不可更改。
% p1 V! E1 p* L+ x; Y% J2 B
3 n4 E; L, v$ [; r& Z+ Fint i = 1;int j = 2;1 X9 X& ?( o% K' }8 j' y" L" P7 A5 _
const int *p1 = &i;int* const p2 = &j;
0 [9 A% K6 q: ]' S! u上面定义了两个指针p1和p2,区别是const后面是指针本身还是指向的内容。
5 J) w- ]3 S9 q; A; Z# t8 ?
0 F- S/ g9 s# ^! L: M7 W5 F在定义1中const限定的是*p1,即其指向空间的值不可改变,若改变其指向空间的值如*p1=10,则程序会报错;但p1的值是可以改变的,对p1重新赋值如p1=&k是没有任何问题的。
4 z' f; i/ P+ Q8 l0 g" w% {5 W" l7 l
- V4 D& i) B' T. P) h5 R, V在定义2中const限定的是指针p2,若改变p2的值如p2=&k,程序将会报错;但*p2,即其所指向空间的值可以改变,如*p2=20是没有问题的,程序正常执行。3 t. M5 w r% O
7 k/ V2 d" \$ A1 ]6 O5 J& y! E
+ E8 m- ?3 x$ I5 X* M& ^! Z1 l8 n4、 修饰函数参数7 n0 Z# e( S) L) C
const关键字修饰函数参数,对参数起限定作用,防止其在函数内部被修改,所限定的函数参数可以是普通变量,也可以是指针变量。* g# m' W3 B% z7 B3 k% f# C# s
$ D" v% ^# _: q; B1 G. z
void fun(const int i){ …… i++; //对i的值进行了修改,程序报错}
4 h8 w- T1 s$ I3 D# d常用的函数如strlen。 T$ n9 w: A; ~5 ~
3 w- o; t6 L$ }2 J- A* p: f* s
size_t strlen(const char *string);
- q/ M/ X9 m4 u- ?8 Kconst在库函数中使用非常普遍,是一种自我保护的安全编码思维。
, d- E' i0 D* A6 L3, T; e8 D$ s2 l J
static关键字
! F, D p' L4 f1、static修饰全局变量,该变量只在本文件内被访问,不能在其他文件被直接访问。4 U) X+ e: ]+ q m3 g e
! \3 ^4 q9 o/ Y8 s: K7 _
2、static修饰函数,该函数只能在本文件内被访问,不能被其他文件访问。但是可以通过嵌套的方式调用,变相的封装的表现。
( }* n# ?5 L; a. g T% }+ Y4 t* x/ j6 i& z, W M( x) D
3、static修饰局部变量,更改该局部变量的生命周期。8 s$ U8 K8 `% T( u' h$ C
生命周期:将临时变量的生命周期变成全局变量的生命周期。作用域不变:作用域仍然是在本代码块内。$ O& u( }4 b g0 Q6 N. x* E
! N0 }2 \8 r1 ]/ y! X
4
+ \3 v. d1 T' W$ [; Gstruct与union5 T& q+ d0 ~$ h6 u
可以使用struct结构体来存放一组不同类型的数据。- |: \' X( B7 I/ X6 [
6 A. ?2 n# {. X7 w/ X' _struct 结构体名{ 结构体所包含的变量或数组};4 c- M$ B9 [. v" X; w1 J l8 Y
结构体是一种集合,它里面包含了多个变量或数组,它们的类型可以相同,也可以不同,每个这样的变量或数组都称为结构体的成员,通常我们使用结构体定义和解析协议,如下所示:7 F8 D2 k( X% d/ G& ^
8 G& a; q9 M" p2 K5 Z i// WiFi接收数据帧,控制切换模式#pragma pack(1)typedef struct receive_data_mode_t{ uint8_t device_head; // 数据帧头:0XA0+功能码(FUNCTION_ID3),A款产品智能插座 uint16_t device_len; // 数据包总长度 uint16_t device_id; // 节点ID 0X0001~0XFFFE char software_version[15]; // 软件版本 SMART_SW_A1_1.0 A款产品软件1.0版本 char hardware_version[15]; // 硬件版本 SMART_HW_A1_1.0 A款产品硬件1.0版本 uint8_t switch_mode; // 切换模式 0:运行模式,1:配置模式,2:节点升级,3:节点重启 uint16_t crc; // 校验位}ReceiveData_Mode_t;#pragma pack()* o7 x' P) p6 H D+ z
union共用体关键字,定义union下面的成员变量共享一块内存,每一个成员在任一时刻有且只有一个成员使用此块内存。
) E, M% h2 @4 k' E4 t# B0 P1 M
union 共用体名{ 成员列表};
: h) m1 g& b$ N4 B* a& l$ k+ e! a* H8 n8 \结构体和共用体的区别在于:结构体的各个成员会占用不同的内存,互相之间没有影响;而共用体的所有成员占用同一段内存,修改一个成员会影响其余所有成员。
* s7 L5 ?, F8 F: X3 ^通常使用共用体做一些标志位操作,例如以下示例,可以非常灵活的访问Val中的bit位。$ l6 c2 q% }5 ^% A, {
# j/ k. o! L" I1 o4 ~& 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;+ }' k% v8 I2 W: e7 O; p
或者使用共用体实现单字节与多字节的转化和拼接,如下所示:
2 K9 L o0 z' b. I: W5 Z
1 m: |3 t) t" b0 V+ `; W9 k#include "stdio.h"
* t5 k8 X1 S7 q) i; W1 Itypedef struct{ union { struct { unsigned char low; unsigned char high; }; unsigned short result; };}test_t;- B) }* V$ Q p# x6 d# E9 `" R
int main(int argc, char *argv[]){ test_t hello;& n+ W3 B8 j9 U- O7 [$ S" f
hello.high=0x12; hello.low=0x34;3 H! l3 y1 U% s- _! @8 |+ _* `
printf("result=%04X\r
. Z4 G/ Z- Z' t: ~$ g1 _4 D' l",hello.result);//输出 result=1234 - [' e* ?# O- `6 Z3 ]
return 0;}5 i& D* \4 t% S2 J1 A/ x3 K$ w
4 L h8 E' z8 [9 u9 q8 o5
- e+ J, W8 I6 K: G" c: m预定义标识符
6 p8 C. a5 t3 A6 |, O ~一般编译器都支持预定义标识符,这些标识符结合printf等打印信息帮助程序员调试程序是非常有用的,一般编译器会自动根据用户指定完成替换和处理。+ E: P t% D3 p( ?2 X& C# T5 i
! P0 f/ d/ n3 R3 [ Z) q' \8 k% G
常用的预定义标识符如下所示:
* i: Z; I! b% b) F& r* k. j( Q2 v" q( P5 d
__FILE__ //表示编译的源文件名__LINE__ //表示当前文件的行号__FUNCTION__ //表示函数名__DATE__ //表示编译日期__TIME__ //表示编译时间
5 K* f0 S. {* ?; O2 @在Debug打印日志时候经常会用到,如下所示:# Q+ z. s6 U9 q6 z7 M5 ?6 g
5 @& c9 U4 V( ]" N% ?& s6 @
printf("file:%s,line:%d,date:%s,time:%s",__FILE__,__LINE__,__DATE__,__TIME__);7 r: f8 m4 P4 a( l! g* G, T: G
% L) r+ o, ?! u9 ^/ E/ s" @
6
6 ^' D/ E1 T: t# @+ g' S& X#与##: w! @) X; Q/ ]! T- J
#:是一种运算符,用于带参宏的文本替换,将跟在后面的参数转成一个字符串常量。% K3 w; V. t: u- @& x
9 ^! Y& d) X5 C& [9 R##:是一种运算符,是将两个运算对象连接在一起,也只能出现在带参宏定义的文本替换中。( y, e4 F( `/ w* [! u
' V+ F% ?8 O' A# f" ?, O0 c#include "stdio.h"
2 O/ r- y; j. J' U2 P! d0 I#define TO_STR(s) #s#define COMB(str1,str2) str1##str2
8 e; I7 p: p2 ^int main(int argc, char *argv[]){ int UART0= 115200;( |7 R+ m: n8 {5 W: ~+ t2 y( C; S
printf("UART0=%d
1 R) B2 Z+ e4 E, l- m* {", COMB(UART, 0));//字符串合并为变量UART0 printf("%s( w. Z: h# {5 Q' r
", TO_STR(3.14));//将数字变成字符串: b. q4 T) r, [+ ~
return 0;}
9 n1 y6 t$ ~; v) C" T4 W9 O7 [
' N. y9 b5 ?) |# @/ s6 g7* ~( F: z. [" X; J k% Y7 N
void 与 void*关键字, o3 k6 ]* a- Z% Q% _& z
void表示的是无类型,不能声明变量或常量,但是可以把指针定义为void类型,如void* ptr。void* 指针可以指向任意类型的数据,在C语言指针操作中,任意类型的数据地址都可转为void* 指针。因为指针本质上都是unsigned int。
8 o0 V: S- u) S% R3 X# f- @
0 }! C' ~! ^' b3 a8 O4 q" H0 ^+ X常用的内存块操作库函数:3 z& ?6 {$ |+ T+ v, e$ ?3 E
8 V0 f/ d' Q* H4 Y
void * memcpy( void *dest, const void *src, size_t len );void * memset( void *buffer, int c, size_t num);( ]0 H" b4 z" J+ O; M' Y+ T
数据指针为void* 类型,对传入任意类型数据的指针都可以操作。另外其中memcpy第二个参数,const现在也如前文所述,拷贝时对传入的原数据内容禁止修改。
5 E, `6 y2 w5 L5 r4 o& `% S( v1 L2 \3 b; P) e2 H9 M
特殊说明,指针是不能使用sizeof求内容大小的,在ARM系统固定为int 4字节。对于函数无输入参数的,也尽量加上void,如下所示:$ r1 w- s. r, B0 W2 i9 g
4 A7 z! A& p/ ^4 I# F8 zvoid fun(void);
2 f- ?4 I/ {2 l8 |( e" t/ n8 y4 M- c/ |( G$ O
8: p1 A1 J) l7 w+ K
weak关键字( Y4 y; J2 j m; \0 n
一般简化定义如下所示:) S4 p$ @% ?' F: v3 Y
, l& G2 u% b" r m! S4 C
#define _WEAK __attribute__((weak))
3 j7 B* q) r- P' @& q函数名称前面加上__WEAK属性修饰符称为“弱函数”,类似C++的虚函数。链接时优先链接为非weak定义的函数,如果找不到则再链接带weak函数。( w- z# C+ {5 g
* @2 ]. C% b) g1 `. w; u$ I
_WEAK void fun(void) { //do this} . b6 {2 w1 U, G! P! a* |% H- a; _9 n
//不在同一个.c,两同名函数不能在同一个文件void fun(void) { //do that}
; W& b3 S4 P1 i" M! W这种自动选择的机制,在代码移植和多模块配合工作的场景下应用较多。例如前期移植代码,需要调用某个接口fun,但当前该接口不存在或者未移植完整使用,可以使用weak关键字定义为空函数先保证编译正常。9 G" x ]: s' p& o3 m
" U$ K7 h( B: u' {! L- A& q( i后续移植完成实现了fun,即软件中有2个fun函数没有任何错误,编译器自动会识别使用后者。当然也粗暴的#if 0屏蔽对fun的调用,但要确保后续记得放开。: T0 f* g, E9 Z. @7 `8 Y/ F: J7 y
( q; g# r. H/ B1 c. ?
1yxnknbkizw64018782842.png
0 v# f) s. l4 o5 X3 t
往期推荐什么是内存碎片?0 f! l4 d9 Q5 d) q
详解UDS CAN诊断:什么是UDS(ISO 14229)诊断?
9 r) I$ X v. q6 d7 u) }磁耦合共振无线供电装置
' b- C$ a$ D5 E: U2 ]C语言:十六进制(HEX)和浮点类型(float、double)转换
_; C7 K$ V& \! ^HarmonyOS 分布式多端应用一站式开发平台(DevEco Studio 安装)1 u C( x; }0 |/ b7 c
/ O- p8 u6 Z) X7 r" B
- i& Z- I; [( u; w! Y9 m# J o
ikto4m4zhwe64018782942.jpg
+ C- u9 }. ~5 w
jvqqbumgoef64018783042.gif
3 m! W' y; {; Y; ~5 E点击阅读原文,更精彩~ |
|