深入淺出變長(zhǎng)結(jié)構(gòu)體
1、 問題的引出
項(xiàng)目中用到數(shù)據(jù)包的處理,但包的大小是不固定的,其長(zhǎng)度由包頭的2字節(jié)決定。比如如下的包頭:88 0f 0a ob cd ef 23 00 。長(zhǎng)度由頭2個(gè)字節(jié)880f決定,考慮字節(jié)序,轉(zhuǎn)為0f88,轉(zhuǎn)為10進(jìn)制3976個(gè)字節(jié)的包長(zhǎng)度。
這個(gè)時(shí)候存儲(chǔ)包的時(shí)候,一方面可以考慮設(shè)定包的大小固定:如4K=4*1024=4096個(gè)字節(jié),因?yàn)樽畲蟀L(zhǎng)不可能超過4k,但該方法的有缺陷,存在一種極端就是包最小僅含包頭不含數(shù)據(jù)域,此時(shí)包為8個(gè)字節(jié),浪費(fèi)了4096-8 =4088個(gè)字節(jié)的存儲(chǔ)空間。另一方面考慮有沒有一種方法能根據(jù)長(zhǎng)度進(jìn)行存儲(chǔ),或者說初始不分配長(zhǎng)度,計(jì)算出了長(zhǎng)度后再分配存儲(chǔ)呢。而實(shí)際項(xiàng)目中正是通過包頭計(jì)算出了包的整體大小的。
這就引出了變長(zhǎng)結(jié)構(gòu)體的概念。
2、 什么叫變長(zhǎng)結(jié)構(gòu)體?
如下所示:
那結(jié)構(gòu)體是怎么實(shí)現(xiàn)可變長(zhǎng)的呢?如上所示,請(qǐng)注意看結(jié)構(gòu)體中的最后一個(gè)元素,一個(gè)沒有元素的數(shù)組。我們可以通過動(dòng)態(tài)開辟一個(gè)比結(jié)構(gòu)體大的空間,然后讓buffer去指向那些額外的空間,這樣就可以實(shí)現(xiàn)可變長(zhǎng)的結(jié)構(gòu)體了。更為巧妙的是,我們甚至可以用nsize存儲(chǔ)字符串buffer的長(zhǎng)度。
并且,上述的結(jié)構(gòu)體可以擴(kuò)展,比如筆者項(xiàng)目中遇到的存儲(chǔ)數(shù)據(jù)包,前面可能類似包頭的部分(存儲(chǔ)類型、長(zhǎng)度等信息),而后面buffer則存儲(chǔ)數(shù)據(jù)部分。
同時(shí),需要引起注意的:ISO/IEC 9899-1999里面,這么寫是非法的,這個(gè)僅僅是GNU C的擴(kuò)展,gcc可以允許這一語法現(xiàn)象的存在。但最新的C/C++不知道是否可以,我沒有測(cè)試過。C99允許。
3、變長(zhǎng)結(jié)構(gòu)體的好處體現(xiàn)在哪?
可能有的同學(xué)會(huì)問到,1引出部分如果說定義定長(zhǎng)數(shù)組浪費(fèi)空間,定義一個(gè)指針不也能指向變長(zhǎng)的數(shù)據(jù)域部分嗎?
是的,是可以實(shí)現(xiàn)的。那么我們就對(duì)比下有什么不同。
結(jié)構(gòu)體1:s_one,用指針指向數(shù)據(jù)域部分;
結(jié)構(gòu)體2:s_two, 用[0]的數(shù)組;
結(jié)構(gòu)體3:s_three, 因?yàn)橛械木幾g器不支持[0],我們用[1]來表示;多了些存儲(chǔ)。
筆者vc6.0的編譯器會(huì)有如下的警告:
運(yùn)行結(jié)果如下:
對(duì)比結(jié)果,我們能發(fā)現(xiàn):
<1> 存儲(chǔ)大小方面:s_two的存儲(chǔ)較s_one、s_three都要少,[0]的好處,即用指針的方式需要多開辟存儲(chǔ)空間的。
<2> 數(shù)據(jù)連續(xù)存儲(chǔ)方面:s_one明顯數(shù)據(jù)域是單獨(dú)開辟的空間,與前的nsize不在連續(xù)的存儲(chǔ)區(qū)域,而s_two,s_three則在連續(xù)的存儲(chǔ)空間下。
<3>釋放內(nèi)存方面:顯然s_one的指針的方式,需要先釋放數(shù)據(jù)域部分,才能釋放指向結(jié)構(gòu)體的指針變量;而s_two,s_three可以直接釋放。
總結(jié)如下:
結(jié)構(gòu)體最后使用0或1的長(zhǎng)度數(shù)組的原因,主要是為了方便的管理內(nèi)存緩沖區(qū),如果你直接使用指針而不使用數(shù)組,那么,你在分配內(nèi)存緩沖區(qū)時(shí),就必須分配結(jié)構(gòu)體一次,然后再分配結(jié)構(gòu)體內(nèi)的指針一次,(而此時(shí)分配的內(nèi)存已經(jīng)與結(jié)構(gòu)體的內(nèi)存不連續(xù)了,所以要分別管理即申請(qǐng)和釋放)。
而如果使用數(shù)組,那么只需要一次就可以全部分配出來,反過來,釋放時(shí)也是一樣,使用數(shù)組,一次釋放,使用指針,得先釋放結(jié)構(gòu)體內(nèi)的指針,再釋放結(jié)構(gòu)體。還不能顛倒次序。
其實(shí)變長(zhǎng)結(jié)構(gòu)體就是分配一段連續(xù)的的內(nèi)存,減少內(nèi)存的碎片化,簡(jiǎn)化內(nèi)存的管理。
4、變長(zhǎng)結(jié)構(gòu)體的應(yīng)用
<1>Socket通信數(shù)據(jù)包的傳輸;
<2>解析數(shù)據(jù)包,如筆者遇到的問題。
<3>其他可以節(jié)省空間,連續(xù)存儲(chǔ)的地方等。
未盡事宜,后續(xù)補(bǔ)上……
2013/9/22pm21:36思于家中床前
作者:銘毅天下
轉(zhuǎn)載請(qǐng)標(biāo)明出處,原文地址:http://blog.csdn.net/laoyang360/article/details/11908731
如果感覺本文對(duì)您有幫助,請(qǐng)點(diǎn)擊‘頂’支持一下,您的支持是我堅(jiān)持寫作最大的動(dòng)力,謝謝!
聯(lián)系客服