文章目錄
語(yǔ)法形式
特性
如何使用
特性驗(yàn)證
我在項(xiàng)目中的使用
總結(jié)
點(diǎn)擊下方閱讀原文可訪問(wèn)文中超鏈接
引用外網(wǎng)的一段內(nèi)容(原文鏈接):
Flexible Array Member(FAM) is a feature introduced in the C99 standard of the C programming language.
For the structures in C programming language from C99 standard onwards, we can declare an array without a dimension and whose size is flexible in nature.
Such an array inside the structure should preferably be declared as the last member of structure and its size is variable(can be changed be at runtime).
The structure must contain at least one more named member in addition to the flexible array member.
有可能你之前也聽說(shuō)過(guò)柔性數(shù)組,但還并未在項(xiàng)目中實(shí)際用過(guò),剛好我最近做一個(gè)項(xiàng)目就用到了柔性數(shù)組,或許可以給你一些啟發(fā)。
柔性數(shù)組是C99標(biāo)準(zhǔn)引入的特性,所以當(dāng)你的編譯器提示不支持的語(yǔ)法時(shí),請(qǐng)檢查你是否開啟了C99選項(xiàng)或更高的版本支持。
語(yǔ)法形式
struct vectord {
short len; // 必須至少有一個(gè)其它成員
char arr[]; // 柔性數(shù)組必須是結(jié)構(gòu)體最后一個(gè)成員(也可是其它類型,如:int、double、...)
};
特性
用
sizeof
計(jì)算結(jié)構(gòu)體大小時(shí),柔性數(shù)組成員是不計(jì)入結(jié)果的。即:
// 假設(shè)short類型占用兩個(gè)字節(jié)
結(jié)構(gòu)體大小 = sizeof(struct vectord) = 2 + 0;
整個(gè)結(jié)構(gòu)體的內(nèi)存空間是連續(xù)的。
如何使用
定義成結(jié)構(gòu)體變量。如果定義成變量的話,那么柔性數(shù)組是操作不了的,因?yàn)闊o(wú)法為其分配內(nèi)存,強(qiáng)行訪問(wèn)的話會(huì)產(chǎn)生段錯(cuò)誤。
struct vectord vec;
定義成結(jié)構(gòu)體指針。這才是正確操作。
struct vectord *pvec;
// 分配內(nèi)存(假設(shè)需要一個(gè)20個(gè)元素的數(shù)組)
pvec = malloc(sizeof(struct vectord) + 20 * sizeof(*pvec->arr));
特性驗(yàn)證
先來(lái)看第一個(gè)特性,使用如下的代碼測(cè)試結(jié)構(gòu)體的大小。
struct vectord {
short len;
char arr[];
};
int main(int argc,char *argv[])
{
printf("The size of structure is:%ld\r\n",sizeof(struct vectord));
return 0;
}
測(cè)試結(jié)果,柔性數(shù)組確實(shí)沒(méi)有參與計(jì)算:
The size of structure is:2
下面驗(yàn)證第二個(gè)特性。這里與結(jié)構(gòu)體內(nèi)嵌指針的形式進(jìn)行對(duì)比。
struct vectord {
short len;
char arr[];
};
struct test {
short len;
char *p;
};
int main(int argc,char *argv[])
{
struct vectord *pvec;
struct test *pt;
pvec = malloc(sizeof(struct vectord) + 20);
pt = malloc(sizeof(struct test));
pt->p = malloc(20);
printf("%p,%p\r\n",&pvec->len,&pvec->arr);
printf("%p,%p\r\n",&pt->len,&pt->p);
return 0;
}
測(cè)試結(jié)果,包含柔性數(shù)組成員的結(jié)構(gòu)體內(nèi)存確實(shí)是連續(xù)的,而內(nèi)嵌指針的方式卻不一定:
0x560511a0f670,0x560511a0f672
0x560511a0f690,0x560511a0f698
我在項(xiàng)目中的使用
項(xiàng)目需求是串口1接收到一幀數(shù)據(jù)(不定長(zhǎng))之后,我需要在這幀數(shù)據(jù)的前面再封裝進(jìn)一幀數(shù)據(jù),然后將組成的一幀新的數(shù)據(jù)通過(guò)串口2再發(fā)出去。即:
串口1->1234567
|
V
組幀->abcdefg1234567
|
V
abcdefg1234567->串口2
這里有以下常用的方法:
直接分兩次發(fā)送,調(diào)用兩次串口發(fā)送函數(shù)就行了。
定義一個(gè)大數(shù)組,將兩幀合并后再發(fā)送。
暫時(shí)想不到還有什么其它好方法。
上面兩種方法都可以實(shí)現(xiàn),第一種需要調(diào)用兩次串口發(fā)送函數(shù),第二種則是空間置換,都不是很好的方案。
這個(gè)時(shí)候柔性數(shù)組就派上用場(chǎng)了,根據(jù)其特性,內(nèi)存是動(dòng)態(tài)的可按需分配,不會(huì)額外浪費(fèi)空間,而且其內(nèi)存空間是連續(xù)的,所以可以像數(shù)組一樣通過(guò)下標(biāo)訪問(wèn);這兩個(gè)特性剛好就可以將上面兩種方案的優(yōu)點(diǎn)結(jié)合到一起。所以可以使用如下的方案:
#pragma pack(1)
struct vectord {
uint16_t packet_len;
char buffer1[4];
char buffer2[];
};
#pragma pack()
void uart2_send(uint8_t *data,uint32_t len);
int main(int argc,char *argv[])
{
struct vectord *pvec;
pvec = malloc(sizeof(struct vectord) + 4);
// 模擬組幀
pvec->packet_len = 2 + 4 + 4;
strncpy(pvec->buffer1,"abcd",4);
strncpy(pvec->buffer2,"1234",4);
// 模擬硬件串口的發(fā)送
uart2_send((uint8_t *)pvec,pvec->packet_len);
return 0;
}
void uart2_send(uint8_t *data,uint32_t len)
{
uint32_t i = 0;
printf("%02x %02x ",data[1],data[0]);
i += 2;
for(;i < len;i++)
{
printf("%c",data[i]);
}
printf("\r\n");
}
測(cè)試結(jié)果:
00 0a abcd1234
總結(jié)
柔性數(shù)組還有很多適合使用的場(chǎng)合,我這里只是其中一種,只要符合其特性的都可以采用柔性數(shù)組來(lái)完成。善于利用各種語(yǔ)法特性,可以將復(fù)雜的代碼簡(jiǎn)單化,也更能檢測(cè)自己是否已經(jīng)掌握了其真正的用法。