之前分享的文章:嵌入式 C 語言知識點(diǎn),掩碼結(jié)構(gòu)體,似乎有些爭議?
不知道是因?yàn)楹甑膶?shí)現(xiàn)看不懂還是用法不懂?掩碼結(jié)構(gòu)體宏的實(shí)現(xiàn)本質(zhì)上就是使用一個(gè)掩碼數(shù)組 chMask 把結(jié)構(gòu)體保護(hù)起來。
用法可以結(jié)合大佬的PLOOC使用示例及其基于C語言的面向?qū)ο缶幊?傻孩子.pdf(本公眾號聊天界面回復(fù):基于C語言的面向?qū)ο缶幊?/strong>)來一起看:
https://github.com/GorgonMeducer/PLOOC
同樣,也可以結(jié)合使用不完全類型(Incomplete Types)來保護(hù)結(jié)構(gòu)體的方式一起看一下,這個(gè)之前有分享過。這里也一起分享下。
C語言中使用不完全類型(Incomplete Types)來保護(hù)結(jié)構(gòu)體的方式,主要涉及到在聲明結(jié)構(gòu)體時(shí)不提供完整的定義,僅在需要時(shí)(如在其源文件中)才給出完整的定義。這種方式的的優(yōu)點(diǎn)和缺點(diǎn):
優(yōu)點(diǎn):
封裝性增強(qiáng):使用不完全類型可以在一定程度上隱藏結(jié)構(gòu)體的內(nèi)部細(xì)節(jié),防止外部代碼直接訪問結(jié)構(gòu)體的成員,從而提高代碼的封裝性和安全性。
模塊間解耦:通過不完全類型聲明,可以在多個(gè)模塊之間傳遞結(jié)構(gòu)體的指針,而無需暴露結(jié)構(gòu)體的完整定義。這有助于減少模塊間的耦合度,使得系統(tǒng)更加靈活和易于維護(hù)。
缺點(diǎn):
使用限制:不完全類型有一些使用上的限制,比如不能直接使用sizeof
運(yùn)算符來獲取不完全類型的大?。ㄒ?yàn)?a class="article-link" target="_blank" href="/baike/1572830.html">編譯器不知道其完整定義)。這可能導(dǎo)致在需要知道結(jié)構(gòu)體大小的情況下無法使用不完全類型。
容易出錯(cuò):如果在使用不完全類型時(shí)沒有正確地提供其完整定義,或者在多個(gè)地方提供了不一致的定義,都可能導(dǎo)致編譯錯(cuò)誤或運(yùn)行時(shí)錯(cuò)誤。
什么是不完全類型?
C/C++中不完全類型有三種不同形式:void、未指定長度的數(shù)組以及具有非指定內(nèi)容的結(jié)構(gòu)和聯(lián)合。使用不完全類型的指針或引用,不需要知道類型的全部內(nèi)容。比如:
我們常用以下方式聲明數(shù)組:
extern?int?array[];
此時(shí)的array就是一個(gè)不完全類型的數(shù)組,一般這樣的數(shù)組聲明會(huì)放在.h中,而其定義放在.c中,在定義的時(shí)候在給出數(shù)組的具體長度,若之后有需要改變數(shù)組的長度時(shí),直接改.c里的就可以,對外的.h就保持原樣不用修改。
用數(shù)組來說明可能還是有點(diǎn)不太好理解,下面我們用結(jié)構(gòu)體的例子來做說明。
在此之前,我們先思考一個(gè)問題,我們的結(jié)構(gòu)體實(shí)體是在頭文件中定義還是源文件中定義呢?
實(shí)際上,在頭文件、源文件中定義都可以
。
下面我們以一個(gè)動(dòng)態(tài)數(shù)組的管理
為例來做一些演示說明。
動(dòng)態(tài)數(shù)組,是相對于靜態(tài)數(shù)組而言。靜態(tài)數(shù)組的長度是預(yù)先定義好的,在整個(gè)程序中,一旦給定大小后就無法改變。而動(dòng)態(tài)數(shù)組則不然,它可以隨程序需要而重新指定大小。
動(dòng)態(tài)數(shù)組的內(nèi)存空間是從堆(heap)上分配(即動(dòng)態(tài)分配)的。是通過執(zhí)行代碼而為其分配存儲(chǔ)空間。當(dāng)程序執(zhí)行到這些語句時(shí),才為其分配。程序員自己負(fù)責(zé)釋放內(nèi)存。使用動(dòng)態(tài)數(shù)組的優(yōu)點(diǎn)是可以根據(jù)用戶需要,有效利用存儲(chǔ)空間。
(1)結(jié)構(gòu)體實(shí)體定義在頭文件中
比如我們本次的demo有如下三個(gè)文件:
此時(shí)dynamic_array.h
的內(nèi)容如下:
我們創(chuàng)建了一些接口函數(shù)來操作DA對象,我們希望他人可以使用我們的這些接口來操作數(shù)據(jù)。并且,一般我們使用其它人寫的代碼時(shí),一般也是優(yōu)先找到相關(guān)頭文件,然后調(diào)用頭文件里提供的對外接口函數(shù)。
但是,從這個(gè)頭文件中,我們不僅僅看到了一些對外接口,還可以看到結(jié)構(gòu)體實(shí)體。于是乎,可能就有些人寫出這樣的代碼:
命名有接口可以用,卻偏偏有人喜歡直接操作數(shù)據(jù),這是比較容易出錯(cuò)的做法。而且調(diào)用者推鍋的理由很充足:你暴露數(shù)據(jù)給我,我為什么不可以直接操控你的數(shù)據(jù),我就不喜歡用你提供的接口,咋的。。。
所以dynamic_array.h
的提供者還是得背鍋。
(2)結(jié)構(gòu)體實(shí)體定義在源文件中
為了不被推鍋,我們把我們的頭文件改為:
此時(shí),這里的dynamic_array_def結(jié)構(gòu)類型就是一個(gè)不完全類型。
我們把結(jié)構(gòu)體實(shí)體定義挪到源文件中,這時(shí)候調(diào)用者看不到dynamic_array_def里有什么數(shù)據(jù)了,間接得就可以強(qiáng)迫調(diào)用者使用我們提供的接口了。此時(shí)如果出問題被推鍋,那我們也樂意接鍋,樂意查找問題呀。
最后,順便貼一下本demo工程完整代碼,有需要的朋友自?。?/p>
dynamic_array.h:
/*?公眾號:嵌入式大雜燴?*/
#ifndef?__DYNAMIC_ARRAY_H
#define?__DYNAMIC_ARRAY_H
/*?結(jié)構(gòu)體“重命名”?*/
typedefstruct?dynamic_array?dynamic_array_def;
/*?初始化dynamic_array?*/
dynamic_array_def?*DA_Init(void);
/*?銷毀dynamic_array?*/
voidDA_Clean(dynamic_array_def?*pThis);
/*?設(shè)置dynamic_array長度?*/
voidDA_SetSize(dynamic_array_def?*pThis,?unsigned?len);
/*?獲取dynamic_array長度?*/
unsignedDA_GetSize(dynamic_array_def?*pThis);
/*?設(shè)置dynamic_array某元素的值?*/
intDA_SetValue(dynamic_array_def?*pThis,?unsigned?index,?int?value);
/*?獲取dynamic_array某元素的值?*/
intDA_GetValue(dynamic_array_def?*pThis,?unsigned?index,?int?*pValue);
#endif
dynamic_array.c:
/*?公眾號:嵌入式大雜燴?*/
#include?"dynamic_array.h"
#include?<stdlib.h>
/*?創(chuàng)建一個(gè)動(dòng)態(tài)數(shù)組結(jié)構(gòu)體模板?*/
struct?dynamic_array
{
int*array;
unsigned?len;
};
/*?初始化dynamic_array?*/
dynamic_array_def?*DA_Init(void)
{
????dynamic_array_def?*pArray?=malloc(sizeof(dynamic_array_def));
????pArray->array=NULL;
????pArray->len?=0;
}
/*?銷毀dynamic_array?*/
voidDA_Clean(dynamic_array_def?*pThis)
{
free(pThis->array);
????pThis->len?=0;
free(pThis);
}
/*?設(shè)置dynamic_array長度?*/
voidDA_SetSize(dynamic_array_def?*pThis,?size_t?len)
{
????pThis->len?=?len;
????pThis->array=(int*)realloc(pThis->array,?pThis->len*sizeof(int));
}
/*?獲取dynamic_array長度?*/
size_tDA_GetSize(dynamic_array_def?*pThis)
{
return?pThis->len;
}
/*?設(shè)置dynamic_array某元素的值?*/
intDA_SetValue(dynamic_array_def?*pThis,?size_t?index,?int?value)
{
if(index?>?pThis->len)
{
return-1;
}
????pThis->array[index]=?value;
return0;
}
/*?獲取dynamic_array某元素的值?*/
intDA_GetValue(dynamic_array_def?*pThis,?size_t?index,?int?*pValue)
{
if(index?>?pThis->len)
{
return-1;
}
*pValue?=?pThis->array[index];
return0;
}
main.c
/*?公眾號:嵌入式大雜燴?*/
#include?<stdio.h>
#include?<stdlib.h>
#include?"dynamic_array.h"
intmain(void)
{
int?arr_elem?=0;
/*?初始化一個(gè)動(dòng)態(tài)數(shù)組?*/
????dynamic_array_def?*pArray?=?DA_Init();
/*?設(shè)置數(shù)組長度為10?*/
????DA_SetSize(pArray,10);
/*?給數(shù)組元素賦值?*/
for(int?i?=0;?i?<10;?i++)
{
????????DA_SetValue(pArray,?i,?i);
}
/*?遍歷數(shù)組元素并打印?*/
for(int?i?=0;?i?<10;?i++)
{
????????DA_GetValue(pArray,?i,&arr_elem);
printf("%d?",?arr_elem);
}
/*?數(shù)組清理?*/
????DA_Clean(pArray);
return0;
}
編譯、運(yùn)行: