哦!数组还能这么用,学到了!
來(lái)源:公眾號(hào)【編程珠璣】
作者:守望先生
ID:shouwangxiansheng
這個(gè)問(wèn)題源于讀者在閱讀redis源碼時(shí)的一個(gè)疑問(wèn)。
先看下面的代碼,對(duì)于包含動(dòng)態(tài)字符串成員的兩個(gè)結(jié)構(gòu)體Test0和Test1占用空間分別是多少呢?
//來(lái)源:公眾號(hào)【編程珠璣】 //作者:守望先生 #include<stdio.h> struct?Test0 {int?a;int?b;char?*c; }; struct?Test1 {int?a;int?b;char?c[]; }; int?main(void) {printf("sizeof(struct?Test0)?=?%zd\n",sizeof(struct?Test0));printf("sizeof(struct?Test1)?=?%zd\n",sizeof(struct?Test1));return?0; }很多讀者一眼就能看出來(lái),在64位系統(tǒng)上,編譯為64位程序,其輸出結(jié)果為:
16 8對(duì)于Test0的結(jié)果是16,通常沒(méi)有什么疑問(wèn),畢竟4(int)+4(int)+8(指針)= 16,但是對(duì)于后者的結(jié)構(gòu)體占用空間為8字節(jié),有的讀者可能會(huì)有疑問(wèn)。(關(guān)于字節(jié)對(duì)齊,參考《字節(jié)對(duì)齊,看這篇就懂了》)
柔性數(shù)組(flexible array)
實(shí)際上這是在C99中引入的柔性數(shù)組的特性。即結(jié)構(gòu)體的最后一個(gè)成員,可以不完整類(lèi)型(一種缺乏足夠的信息去描述一個(gè)完整對(duì)象的類(lèi)型)的數(shù)組,但它使得整個(gè)結(jié)構(gòu)體的大小就像沒(méi)有這個(gè)成員一樣。但是呢,當(dāng)用結(jié)構(gòu)體通過(guò)這個(gè)名字訪問(wèn)這個(gè)成員時(shí),就像訪問(wèn)一個(gè)普通數(shù)組成員一樣。
如果數(shù)組最終一個(gè)元素都沒(méi)有的話(huà),那么訪問(wèn)這個(gè)數(shù)組將會(huì)是未定義行為了。
正如我們前面所看到的:
struct?Test1 {int?a;int?b;char?c[]; };成員c是一個(gè)數(shù)組,但是并沒(méi)有指定大小,使用sizeof計(jì)算Test1,其占用空間也僅僅是8字節(jié)。
有什么好處?
那么使用柔性數(shù)組有什么好處呢?
內(nèi)存申請(qǐng)和釋放
假設(shè)分別使用兩種類(lèi)型的結(jié)構(gòu)體,存儲(chǔ)16字節(jié)的字符數(shù)據(jù),需要申請(qǐng)內(nèi)存。對(duì)于struct Test0:
strcut?Test0?*t0?=?malloc(sizeof(struct?Test0));//為結(jié)構(gòu)體申請(qǐng)內(nèi)存 t0->c?=?malloc(sizeof(char)?*?16);//為成員指向的數(shù)據(jù)申請(qǐng)內(nèi)存而對(duì)于struct Test1:
strcut?Test1?*t1?=?malloc(sizeof(struct?Test1)?+?sizeof(char)?*?16);看出區(qū)別了嗎?前者需要兩次內(nèi)存申請(qǐng),而后者只需要一次。前者地址不連續(xù)(兩次malloc),后者地址連續(xù)。而你訪問(wèn)成員c的時(shí)候,只需要下面這樣就可以:
t1->c,和普通成員無(wú)異。
要判斷它們的地址是否連續(xù)也非常簡(jiǎn)單,只需要分別打印b和c的地址就可以了。
和內(nèi)存釋放類(lèi)似,前面需要單獨(dú)釋放成員c申請(qǐng)的內(nèi)存,而后者可以一起釋放。
數(shù)據(jù)拷貝
正由于前面的差別,導(dǎo)致數(shù)據(jù)拷貝時(shí),更有區(qū)別。
對(duì)于struct Test0:
這里無(wú)法一次拷貝,因?yàn)樗某蓡Tc是一個(gè)指針類(lèi)型,我們需要的是一份完整拷貝,因此必須拷貝它指向的內(nèi)存。(參考《結(jié)構(gòu)體成員賦值到底是深拷貝還是淺拷貝?》)
但是對(duì)于struct Test1:
memcpy(t0copy,t0,sizeof(strcut?Test1)?+?sizeof(char)?*?16);在這里,由于柔性數(shù)組的內(nèi)存,它的數(shù)據(jù)內(nèi)容和結(jié)構(gòu)體數(shù)據(jù)成員的地址是連續(xù)的,因此可以直接拷貝。
減少內(nèi)存碎片
由于結(jié)構(gòu)體的柔性數(shù)組和結(jié)構(gòu)體成員的地址是連續(xù)的,即可一同申請(qǐng)內(nèi)存,因此更大程度地避免了內(nèi)存碎片。另外由于該成員本身不占結(jié)構(gòu)體空間,因此,整體而言,比普通的數(shù)組成員占用空間要會(huì)稍微小點(diǎn)。
零長(zhǎng)數(shù)組
與柔性數(shù)組功能類(lèi)似,還有一個(gè)0長(zhǎng)數(shù)組,不過(guò)它并不是標(biāo)準(zhǔn)中的,但是它可以實(shí)現(xiàn)類(lèi)似的功能,使用方式如下:
struct?Test1 {int?a;int?b;char?c[0]; };差別在于使得數(shù)組長(zhǎng)度為0。但是由于它并非C標(biāo)準(zhǔn)中的,因此從可移植性考慮,不建議使用這種方式,除非你還無(wú)法使用C99。
總結(jié)
柔性數(shù)組的使用:
位于結(jié)構(gòu)體最后一個(gè)位置
不完整數(shù)組類(lèi)型
不是唯一成員
最后,放張圖,看差別:
?
推薦閱讀:
專(zhuān)輯|Linux文章匯總
專(zhuān)輯|程序人生
專(zhuān)輯|C語(yǔ)言
嵌入式Linux
微信掃描二維碼,關(guān)注我的公眾號(hào)?
總結(jié)
以上是生活随笔為你收集整理的哦!数组还能这么用,学到了!的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 人在旅途之桂林
- 下一篇: 使用Luyten工具反编译jar包