柔性數(shù)組(Redis源碼學(xué)習(xí))

柔性數(shù)組(Redis源碼學(xué)習(xí))

1. 問(wèn)題背景

在閱讀Redis源碼中的字符串有如下結(jié)構(gòu),在sizeof(struct sdshdr)得到結(jié)果為8岸裙,在后續(xù)內(nèi)存申請(qǐng)和計(jì)算中也用到鹏溯。其實(shí)在工作中有遇到過(guò)這種 struct結(jié)構(gòu) + 應(yīng)用數(shù)據(jù)的情況,但沒(méi)有意識(shí)到自己使用的是柔性數(shù)組,在學(xué)習(xí)閱讀Redis代碼中,遇到該方法,就特總結(jié)記錄之在验。

/* * 類型別名,用于指向 sdshdr 的 buf 屬性 */
typedef char * sds;
/* * 保存字符串對(duì)象的結(jié)構(gòu) */
struct sdshdr {    
    // buf 中已占用空間的長(zhǎng)度
    int len;
    // buf 中剩余可用空間的長(zhǎng)度
    int free;
    // 數(shù)據(jù)空間
    char buf[];
};

2. 柔性數(shù)組

柔性數(shù)組(flexible array member)也叫伸縮性數(shù)組成員堵未,這種結(jié)構(gòu)產(chǎn)生與對(duì)動(dòng)態(tài)結(jié)構(gòu)體的去求腋舌。在日常編程中,有時(shí)需要在結(jié)構(gòu)體中存放一個(gè)長(zhǎng)度是動(dòng)態(tài)的字符串(也可能是其他數(shù)據(jù)類型)渗蟹。

一般的做法块饺,是在結(jié)構(gòu)體中定義一個(gè)指針成員,這個(gè)指針成員指向該字符串所在的動(dòng)態(tài)內(nèi)存空間雌芽。在通常情況下刨沦,如果想要高效的利用內(nèi)存,那么在結(jié)構(gòu)體內(nèi)部定義靜態(tài)的數(shù)組是非常浪費(fèi)的行為膘怕。其實(shí)柔性數(shù)組的想法和動(dòng)態(tài)數(shù)組的想法是一樣的想诅。

柔性數(shù)組用來(lái)在結(jié)構(gòu)體中存放一個(gè)長(zhǎng)度動(dòng)態(tài)的字符串。
本文基于redis 的sds.c源碼岛心,進(jìn)行簡(jiǎn)單編碼驗(yàn)證測(cè)試来破,其實(shí)這種柔性數(shù)組,在工作中用到過(guò)忘古,但是沒(méi)有意識(shí)到這是柔性數(shù)組徘禁。

上述struct sdshdr結(jié)構(gòu)中,要注意:最后一個(gè)變量 buf 數(shù)組中髓堪,沒(méi)有長(zhǎng)度送朱,這和自己遇到的正常的使用方式不一樣,新的知識(shí)點(diǎn)
這種用法是C語(yǔ)言中的柔性數(shù)組干旁,上面 的sizeof(sdshdr )結(jié)果是8驶沼,即后面的buf不占空間,只是一個(gè)符號(hào)争群,測(cè)試上面sdshdr結(jié)果如下:

int main(int argc,char **argv){    
    struct sdshdr t;      
    
    printf("int len:%d\n",sizeof(int)); 
    printf("sdshdr len:%d\n",sizeof(struct sdshdr));
      
    printf("Address:\n");
    printf("t\t %p\n", &t);
    printf("t.len\t %p\n", &(t.len));
    printf("t.free\t %p\n", &(t.free)); 
    printf("t.buf\t %p\n", &(t.buf));   
    return 0;   
}

RHEL6.9上執(zhí)行上面代碼塊得到結(jié)果如下:

$ ./sdshdr                  
int len:4
sdshdr len:8
Address:
t        0x7fff9572fa50
t.len    0x7fff9572fa50
t.free   0x7fff9572fa54
t.buf    0x7fff9572fa58

可以看到 t.buf 是該結(jié)構(gòu)的最后的地址回怜,是最后一個(gè)點(diǎn),簡(jiǎn)單圖示如下:


P01.jpg

如果后續(xù)再malloc相關(guān)的內(nèi)存换薄,則就會(huì)在t.buf后面連續(xù),簡(jiǎn)單編寫代碼進(jìn)行驗(yàn)證玉雾。要加入對(duì)應(yīng)的sds.h文件翔试,或者直接將結(jié)構(gòu)定義在main函數(shù)之前。

int main(int argc,char **argv){    
    struct sdshdr t;      
      
    printf("int len:%d\n",sizeof(int)); 
    printf("sdshdr len:%d\n",sizeof(struct sdshdr));
      
    printf("Address:\n");
    printf("t\t %p\n", &t);
    printf("t.len\t %p\n", &(t.len));
    printf("t.free\t %p\n", &(t.free)); 
    printf("t.buf\t %p\n", &(t.buf));   
    
    printf("sizeof(char):\t %d\n", sizeof(char));   
    struct sdshdr *p=(struct sdshdr*)malloc(sizeof(struct sdshdr) + sizeof(char)*8);
    printf("After malloc the struct's size is %d\n",sizeof(struct sdshdr));

    printf("Address:\n");
    printf("p\t %p\n", p);
    printf("p->len\t %p\n", &(p->len));
    printf("p->free\t %p\n", &(p->free));   
    printf("p->buf\t %p,sizeof(p):%d\n", &(p->buf),sizeof(p));  
    
    memset(p,0,sizeof(struct sdshdr) + sizeof(char)*8);
    char *str="Hello";
    memcpy(p->buf,str,strlen(str));
    printf("p->buf:%s\n",p->buf);
    
    char *str1="HelloWorldttttttt";
    memcpy(p->buf,str1,sizeof(char)*8-1);
    printf("p->buf:%s\n",p->buf);
    printf("strlen(p->buf):%d\n",strlen(p->buf));
      return 0; 
}

上述代碼進(jìn)行編譯复旬,獲得可執(zhí)行文件垦缅,執(zhí)行結(jié)果如下:

$ ./sdshdr                  
int len:4
sdshdr len:8
Address:
t        0x7ffea0a8c420
t.len    0x7ffea0a8c420
t.free   0x7ffea0a8c424
t.buf    0x7ffea0a8c428
sizeof(char):    1
After malloc the struct's size is 8
Address:
p        0x1bc3010
p->len   0x1bc3010
p->free  0x1bc3014
p->buf   0x1bc3018,sizeof(p):8
p->buf:Hello
p->buf:HelloWo
strlen(p->buf):7
$

3. 使用方法

從C99開始便支持了不完整類型實(shí)現(xiàn)柔性數(shù)組成員。為什么使用不完整類型呢驹碍?

int a[] = {10};

看到這個(gè)聲明語(yǔ)句失都,我們發(fā)現(xiàn)a[]其實(shí)就是個(gè)數(shù)組記號(hào),不完整類型幸冻,由于賦值語(yǔ)句,所以在編譯時(shí)便確定了數(shù)組的大小咳焚,是一個(gè)完整的數(shù)組類型洽损。
在結(jié)構(gòu)體中便利用不完整類型在運(yùn)行對(duì)動(dòng)態(tài)的數(shù)組進(jìn)行指明。
C99標(biāo)準(zhǔn)的定義如下:

struct Test{
    int a;
    char p[]; // 不只是char類型革半,其他類型同樣也是可以
}

由于聲明內(nèi)存連續(xù)性的關(guān)系碑定,柔性數(shù)組成員必須定義在結(jié)構(gòu)體的最后一個(gè),并且不能是唯一的成員又官。
我們?cè)賮?lái)看一看整個(gè)結(jié)構(gòu)體(包含數(shù)組內(nèi)存的分布情況),進(jìn)行簡(jiǎn)單編碼驗(yàn)證延刘。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Test
{
    int a;
    char p[];
} Test;
int main()
{
    Test *t=(Test*)malloc(sizeof(Test)+sizeof(char)*(10+1));
    printf("sizeof(int):%d,sizeof(Test):%d\n",sizeof(int),sizeof(Test));
    strcpy(t->p,"hello");
    printf("t->p:%s\n", (t->p));
    printf("Address:\n");
    printf("t\t %p\n", t);
    printf("t.a\t %p\n", &(t->a));
    printf("t.p\t %p\n", (t->p));    
    free(t);    //只需要釋放一次內(nèi)存
    return 0;
}

在linux上的執(zhí)行結(jié)果如下:

$ ./sdshdr                  
sizeof(int):4,sizeof(Test):4
t->p:hello
Address:
t        0x7e0010
t.a      0x7e0010
t.p      0x7e0014

4. 小結(jié)

  1. 在結(jié)構(gòu)體中存放一個(gè)長(zhǎng)度是動(dòng)態(tài)數(shù)據(jù)類型時(shí),可以考慮到柔性數(shù)組六敬。
  2. 一般做法碘赖,是在結(jié)構(gòu)體中定義一個(gè)指針成員,這個(gè)指針成員指向所在的動(dòng)態(tài)內(nèi)存空間外构。
  3. 該指針成員普泡,不占結(jié)構(gòu)體空間,只是一個(gè)符號(hào)审编。
  4. 柔性數(shù)組成員必須定義在結(jié)構(gòu)體的最后一個(gè)撼班,并且不能是唯一的成員。

5. 參考文獻(xiàn)

https://www.cnblogs.com/davygeek/p/5748852.html
https://blog.csdn.net/qq_40477151/article/details/78905567
https://www.cnblogs.com/pluviophile/p/7571410.html

本人才疏學(xué)淺垒酬,參考網(wǎng)絡(luò)文章及代碼驗(yàn)證砰嘁,如有錯(cuò)誤不當(dāng)之處,請(qǐng)批評(píng)指正勘究。
如果能為您帶來(lái)一wx點(diǎn)hongmaolinux點(diǎn)幫助矮湘,那將是我的榮幸,歡迎您關(guān)注口糕,轉(zhuǎn)發(fā)推薦板祝,謝謝!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末走净,一起剝皮案震驚了整個(gè)濱河市券时,隨后出現(xiàn)的幾起案子孤里,更是在濱河造成了極大的恐慌,老刑警劉巖橘洞,帶你破解...
    沈念sama閱讀 219,427評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件捌袜,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡炸枣,警方通過(guò)查閱死者的電腦和手機(jī)虏等,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)适肠,“玉大人霍衫,你說(shuō)我怎么就攤上這事『钛” “怎么了敦跌?”我有些...
    開封第一講書人閱讀 165,747評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)逛揩。 經(jīng)常有香客問(wèn)我柠傍,道長(zhǎng),這世上最難降的妖魔是什么辩稽? 我笑而不...
    開封第一講書人閱讀 58,939評(píng)論 1 295
  • 正文 為了忘掉前任惧笛,我火速辦了婚禮,結(jié)果婚禮上逞泄,老公的妹妹穿的比我還像新娘患整。我一直安慰自己,他們只是感情好喷众,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評(píng)論 6 392
  • 文/花漫 我一把揭開白布并级。 她就那樣靜靜地躺著,像睡著了一般侮腹。 火紅的嫁衣襯著肌膚如雪嘲碧。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,737評(píng)論 1 305
  • 那天父阻,我揣著相機(jī)與錄音愈涩,去河邊找鬼。 笑死加矛,一個(gè)胖子當(dāng)著我的面吹牛履婉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播斟览,決...
    沈念sama閱讀 40,448評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼毁腿,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起已烤,我...
    開封第一講書人閱讀 39,352評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤鸠窗,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后胯究,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體稍计,經(jīng)...
    沈念sama閱讀 45,834評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評(píng)論 3 338
  • 正文 我和宋清朗相戀三年裕循,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了臣嚣。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,133評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡剥哑,死狀恐怖硅则,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情株婴,我是刑警寧澤怎虫,帶...
    沈念sama閱讀 35,815評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站督暂,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏穷吮。R本人自食惡果不足惜逻翁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望捡鱼。 院中可真熱鬧八回,春花似錦、人聲如沸驾诈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)乍迄。三九已至管引,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間闯两,已是汗流浹背褥伴。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留漾狼,地道東北人重慢。 一個(gè)月前我還...
    沈念sama閱讀 48,398評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像逊躁,于是被迫代替她去往敵國(guó)和親似踱。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容