|
之前分享的文章:嵌入式 C 语言知识点,掩码结构体,似乎有些争议?
( S4 j# t# X6 H( {# Y/ b不知道是因为宏的实现看不懂还是用法不懂?掩码结构体宏的实现本质上就是使用一个掩码数组 chMask 把结构体保护起来。! A- s _. N: @" ^8 x: x r7 }4 _
用法可以结合大佬的PLOOC使用示例及其基于C语言的面向对象编程-傻孩子.pdf(本公众号聊天界面回复:基于C语言的面向对象编程)来一起看:
( x- }7 r6 ?' k0 {5 s7 Shttps://github.com/GorgonMeducer/PLOOC
& b2 X1 n" O. f
9 O \: B% E" C" r
reo54qc2evn6405241009.png
6 _3 H: n5 t8 ?! m
2p2llbwmkqb6405241109.png
7 D& g. s' H3 r0 H+ b, L5 M# N8 o
同样,也可以结合使用不完全类型(Incomplete Types)来保护结构体的方式一起看一下,这个之前有分享过。这里也一起分享下。
9 g2 c3 |; `3 y5 [( wC语言中使用不完全类型(Incomplete Types)来保护结构体的方式,主要涉及到在声明结构体时不提供完整的定义,仅在需要时(如在其源文件中)才给出完整的定义。这种方式的的优点和缺点:
2 v2 j& p3 l1 R3 d/ g/ l2 p$ s优点:
: k4 j9 F7 a% C5 i( v) f8 u8 Y) a3 E封装性增强:使用不完全类型可以在一定程度上隐藏结构体的内部细节,防止外部代码直接访问结构体的成员,从而提高代码的封装性和安全性。% U7 u4 S3 T. a" E$ }
模块间解耦:通过不完全类型声明,可以在多个模块之间传递结构体的指针,而无需暴露结构体的完整定义。这有助于减少模块间的耦合度,使得系统更加灵活和易于维护。
( }5 X. ?7 c1 W) J6 w4 R4 ?缺点:
/ T6 U' j4 e* W: Y# L3 ^' n使用限制:不完全类型有一些使用上的限制,比如不能直接使用sizeof运算符来获取不完全类型的大小(因为编译器不知道其完整定义)。这可能导致在需要知道结构体大小的情况下无法使用不完全类型。: _4 W. H* f- W c% G
容易出错:如果在使用不完全类型时没有正确地提供其完整定义,或者在多个地方提供了不一致的定义,都可能导致编译错误或运行时错误。6 d, j. V$ g3 [& `& I( [, I
什么是不完全类型?C/C++中不完全类型有三种不同形式:void、未指定长度的数组以及具有非指定内容的结构和联合。使用不完全类型的指针或引用,不需要知道类型的全部内容。比如:
3 P& f9 Q+ J1 V, G我们常用以下方式声明数组:- b& g! t* h R# M
extern int array[];此时的array就是一个不完全类型的数组,一般这样的数组声明会放在.h中,而其定义放在.c中,在定义的时候在给出数组的具体长度,若之后有需要改变数组的长度时,直接改.c里的就可以,对外的.h就保持原样不用修改。
6 E- g- \/ ^$ f) Q# Z用数组来说明可能还是有点不太好理解,下面我们用结构体的例子来做说明。
" X2 I( {) q5 v在此之前,我们先思考一个问题,我们的结构体实体是在头文件中定义还是源文件中定义呢?$ L2 G# F7 V; N& J4 y6 P
实际上,在头文件、源文件中定义都可以。6 D3 C( a( P8 ]( P! }5 q& y3 L
下面我们以一个动态数组的管理为例来做一些演示说明。
3 K9 i7 U5 u0 ]& X动态数组,是相对于静态数组而言。静态数组的长度是预先定义好的,在整个程序中,一旦给定大小后就无法改变。而动态数组则不然,它可以随程序需要而重新指定大小。- a; r0 G" a( o3 V
动态数组的内存空间是从堆(heap)上分配(即动态分配)的。是通过执行代码而为其分配存储空间。当程序执行到这些语句时,才为其分配。程序员自己负责释放内存。使用动态数组的优点是可以根据用户需要,有效利用存储空间。8 O* V- _9 h4 h3 i" R* V
(1)结构体实体定义在头文件中比如我们本次的demo有如下三个文件:3 r4 i; F* X" r. C# T
b5d0g0gogk06405241209.png
6 g0 a3 d- D$ W$ C# f
此时dynamic_array.h的内容如下:
( w9 P0 N3 f/ M- `0 A) B5 I
ztdf04no0ct6405241309.png
% Q9 m# D: A' O6 Q, B s
我们创建了一些接口函数来操作DA对象,我们希望他人可以使用我们的这些接口来操作数据。并且,一般我们使用其它人写的代码时,一般也是优先找到相关头文件,然后调用头文件里提供的对外接口函数。& v/ k* v. g) S7 v) y0 F1 E
但是,从这个头文件中,我们不仅仅看到了一些对外接口,还可以看到结构体实体。于是乎,可能就有些人写出这样的代码:6 h8 S7 d |/ a' }9 e
w4glz1yhiju6405241409.png
( _' l" n6 y- N& ^# f/ Y
明明有接口可以用,却偏偏有人喜欢直接操作数据,这是比较容易出错的做法。而且调用者推锅的理由很充足:你暴露数据给我,我为什么不可以直接操控你的数据,我就不喜欢用你提供的接口,咋的。。。
$ D/ ]9 G) t+ `7 S/ b# A: [
fxdlkb3rdo36405241509.jpg
, k/ K8 J2 B1 ~
所以dynamic_array.h的提供者还是得背锅。9 H& J2 W1 K7 B
(2)结构体实体定义在源文件中为了不被推锅,我们把我们的头文件改为:* F* g+ r; `. U
i135bv25zvf6405241609.png
/ X- p; K" V$ ?' p& q8 h此时,这里的dynamic_array_def结构类型就是一个不完全类型。1 W! |' k* ?5 M+ O
我们把结构体实体定义挪到源文件中,这时候调用者看不到dynamic_array_def里有什么数据了,间接得就可以强迫调用者使用我们提供的接口了。此时如果出问题被推锅,那我们也乐意接锅,乐意查找问题呀。( R% {3 p2 T, q
最后,顺便贴一下本demo工程完整代码,有需要的朋友自取:
! c0 n$ f# q7 w0 z1 r2 hdynamic_array.h:/* 公众号:嵌入式大杂烩 */4 P% u V- h0 X7 x0 f
#ifndef __DYNAMIC_ARRAY_H1 t0 e1 c) ]" x/ ~2 D% F
#define __DYNAMIC_ARRAY_H w5 b$ r0 X; P: s8 x% W# {
/* 结构体“重命名” */5 `# Q4 ^* G5 Q. ]* M1 \: h
typedefstruct dynamic_array dynamic_array_def;* }! F f. l$ Q( w1 n3 ^
/* 初始化dynamic_array */- r% M8 r, i# T8 i6 h/ g
dynamic_array_def *DA_Init(void);
& R9 \) p* L! q/* 销毁dynamic_array */1 d- N; c3 x" n( {& O; |; U1 Z
voidDA_Clean(dynamic_array_def *pThis);
; D! A: y. W7 ~" R7 b) N( l/* 设置dynamic_array长度 */% R6 [! y% @2 S3 g; ?) ~( P
voidDA_SetSize(dynamic_array_def *pThis, unsigned len);) g+ R1 @& T! D6 C+ f: D6 \# A
/* 获取dynamic_array长度 */
) D7 n) q3 F2 CunsignedDA_GetSize(dynamic_array_def *pThis);
) J. Z4 M1 a; m- @* n/* 设置dynamic_array某元素的值 */0 V6 |+ ^: k* A* o$ r( Q- R
intDA_SetValue(dynamic_array_def *pThis, unsigned index, int value);
: ~" J4 j" b3 w5 @- S6 e" c0 I/* 获取dynamic_array某元素的值 */, B6 r$ U6 ]# c5 A7 @& |0 K. C
intDA_GetValue(dynamic_array_def *pThis, unsigned index, int *pValue);
+ J2 J/ ^( _) j8 S/ V i#endifdynamic_array.c:/* 公众号:嵌入式大杂烩 */! j* f9 Q) A- O0 l4 N+ V
#include "dynamic_array.h", w! B& M- P" a' Y
#include 6 z0 ?6 |1 S' X5 i4 l
/* 创建一个动态数组结构体模板 */6 Y* c- t0 N1 c) x: p( J* J
struct dynamic_array( v7 g; ` U! ?& R" V" T+ Q
{
9 a+ W9 }% C2 r i* o% Kint*array;! Y( ]1 m# E) d! g" i/ V% s* L) f1 ?
unsigned len;
1 V4 O9 m3 ~0 F! H};+ Y v6 ^9 x- M4 k k: k ^
/* 初始化dynamic_array */
0 P' v8 x+ F# F. O% ldynamic_array_def *DA_Init(void)5 W" X2 A" l# G" s. I1 ~* Y& r! y
{
+ h6 P8 h/ I3 U) j! Z+ K dynamic_array_def *pArray =malloc(sizeof(dynamic_array_def));$ i% H+ w* k1 o- C
pArray->array=NULL;
! G$ z2 r8 `0 q. c3 R" o- P i pArray->len =0;
& U7 h: t1 j$ W# t0 z}# v7 x- z- C* V. F8 _/ F% r0 t, a: {
/* 销毁dynamic_array */5 t, X1 ?$ ~! { v# Y
voidDA_Clean(dynamic_array_def *pThis)# @. P+ Q9 y+ f, a9 q, h
{
2 T$ k: X* W$ K1 U% X& ffree(pThis->array);
$ L: w# C: ^9 C1 R1 E; N5 `4 G pThis->len =0;) [9 V) X. ]8 \9 j! G0 \
free(pThis);
" l" h. [9 b; `) M7 V% D; }* S}; |- B' ^3 r" j4 m4 d" y
/* 设置dynamic_array长度 */2 {# w4 t7 O+ M7 Q
voidDA_SetSize(dynamic_array_def *pThis, size_t len)
w! |4 N& k& a# p{$ g5 U$ e6 }) D" B/ f& q6 S( m
pThis->len = len;
6 L: _# n; y/ `8 M. S pThis->array=(int*)realloc(pThis->array, pThis->len*sizeof(int));+ S: l2 G! e7 d* \5 i% q. y2 Y k$ B9 L
}" @# k/ R2 A0 H: m- V' j) |
/* 获取dynamic_array长度 */
3 o6 C& h7 Z9 G0 m% W9 o2 p/ osize_tDA_GetSize(dynamic_array_def *pThis)) {, s6 @" G- X3 W [% _& E* [: f
{' l2 s4 g! X) y$ r5 g% J
return pThis->len;
( d; _3 e" V7 ?9 P}
1 o/ L' s8 s3 W* g, O2 e/* 设置dynamic_array某元素的值 */
' c7 {- Z9 g) Y' kintDA_SetValue(dynamic_array_def *pThis, size_t index, int value)1 ?7 o. ^! U, q% [
{
# f9 _! G s* P* R' o) s, `% uif(index > pThis->len)+ ]+ o& X* h3 K4 Y5 a: b# }
{2 w' z+ `0 N! D% b( u) f: @
return-1; T9 D/ @. L, u% l
}
' a3 J+ z) {- I% u8 H* O3 H pThis->array[index]= value;
# o: Q& L8 `* t* \6 o9 U9 ^return0;
: q; k1 g( g, F8 x* Q' O0 [}9 }7 Y8 x) ~/ h- u% G% i0 s. i
/* 获取dynamic_array某元素的值 */; J' Q. H5 a7 y& [$ b& h
intDA_GetValue(dynamic_array_def *pThis, size_t index, int *pValue)
( q, Q6 _3 W. ~; m! [{' a' k o8 D) Q4 Y
if(index > pThis->len)$ N% g+ E9 ~. {% @- t
{5 y5 F0 q: a' |' b( m& K/ ]
return-1;! h/ f- \( ^1 ]% }
}
$ M5 q/ O* N1 X1 g7 |: F0 P/ `*pValue = pThis->array[index];
5 |6 b0 g3 C/ o* }9 w6 Xreturn0;
- r$ g' N: p9 R8 I/ c1 E) a6 \}main.c/* 公众号:嵌入式大杂烩 */
" ^7 \0 m; B' v0 M e4 I#include . G; u' c( @* c1 a$ `
#include ! [: X4 L/ f1 O/ e' ~
#include "dynamic_array.h"
- P( F3 U1 _/ F9 }: R0 aintmain(void)
5 ]$ Z6 b5 j6 k; Q3 E M{
/ w+ ]# }1 z; p2 Aint arr_elem =0;
! U. `5 ?2 w- h/* 初始化一个动态数组 */0 Z- ^. j0 _/ L N& ^
dynamic_array_def *pArray = DA_Init();
% r; \ T- W2 ?/* 设置数组长度为10 */3 V3 }4 U+ y0 S( j
DA_SetSize(pArray,10);/ k* n3 ]. E- V, a# J( P, C& ^9 w
/* 给数组元素赋值 */5 k: [/ u7 n; x2 S9 r( N; |# [0 {
for(int i =0; i 10; i++)
$ a) u# _: u4 ~6 o{6 i! f7 @6 d: ]9 q. g
DA_SetValue(pArray, i, i);
7 g* L% o" D0 m}
0 p( v- f% W) i/* 遍历数组元素并打印 */
) b6 d, S4 h$ e7 v$ n# D4 ]for(int i =0; i 10; i++)( T! r3 }; |+ O! U
{
. t5 k! v q2 [7 B' G4 F3 j) ^ DA_GetValue(pArray, i,&arr_elem);) B {& \ Y2 e k2 [2 G$ x
printf("%d ", arr_elem);& A: d; j+ t& H; z3 d+ C& z
}
' Q( s. e" Z+ _: x/* 数组清理 */
0 Y# [6 ^) G/ }2 c/ |2 z DA_Clean(pArray);
# `: r: Z5 L. |return0;9 w% T$ L& r, ?& b
}编译、运行:- |, w* K7 t8 f" v0 X2 e
rbyrxsylfde6405241709.png
2 P# }# C3 l* c0 d3 r
7 K: y( x$ T/ x
-END-0 F& x' U6 }5 L" B. s# |8 p) J
往期推荐:点击图片即可跳转阅读
: d0 e6 l" Z. k# s' m( P( \' J 4 R- _6 |; k2 R5 R: [, G
4 }* _3 s, t) Z% t" |1 A2 l' N
% k& a: g) o! m' O5 B3 P
- ]( d( _ \0 H
jgjpdj5jqam6405241809.jpg
' r, t" Q5 \* F5 Z4 D# m, l2 J3 H
% g4 H4 C# `: o1 H9 H& d% O 嵌入式物联网开发,搭建属于自己的云服务器
/ W" r% t- l9 y( h1 g( w8 {$ j
4 S% w# i5 {, r4 h - n% s/ ]* l f; p2 l' q. T
+ M( l) i8 ?" [0 W5 O+ M0 r ) D* s( F7 x) T/ N5 K& F* v, f3 L
rvbzkewhzxq6405241909.jpg
+ x. ?6 f0 q& b1 \& t
5 A4 J, F% ]4 d- e/ x+ [4 k: E' h8 u 嵌入式 C 语言特性,指定初始化器
/ |/ c8 }/ h- M5 u& j7 h4 G" i8 c0 L
7 C$ |& j6 e: O/ G# ]+ a& i : N" V! V; o+ r) D$ c
: h+ ^/ J4 A- [& [6 J2 |
dm3v4cnooua6405242009.jpg
9 z7 ` ]' L. Q. p* g
) k8 C% e- {* ^- D 嵌入式 C 语言知识点,掩码结构体. D( J) q$ z6 X- R7 v* U/ S
) D$ W; [0 p, P9 `/ {3 g1 g
% q0 ^- X$ R ^& L
8 @0 u* ^5 n, P) f% C 我是老温,一名热爱学习的嵌入式工程师
K0 R0 r5 e: @关注我,一起变得更加优秀! |
|