C柔性数组
?柔性數(shù)組成員
柔性數(shù)組 1、允許結(jié)構(gòu)中包含一個大小可變的數(shù)組,sizeof返回的這種結(jié)構(gòu)大小不包括柔性數(shù)組的內(nèi)存。 2、包含柔性數(shù)組成員的結(jié)構(gòu)要使用malloc()函數(shù)進(jìn)行內(nèi)存的動態(tài)分配。分配的內(nèi)存大于結(jié)構(gòu)體的大小。 3、柔性數(shù)組的定義 typedef strcut type {int i;int a[]; }type_a; 4、type_a *p=(type_a *)malloc(sizeof(type_a)+100*sizeof(int)); 5、p->a[n]直接訪問數(shù)組。 6、free釋放內(nèi)存struct結(jié)構(gòu)中的最后一個元素允許是未知大小的數(shù)組,但是柔性數(shù)組前面必須有一個其他成員。包含柔性數(shù)組成員的結(jié)構(gòu)用malloc()函數(shù)進(jìn)行內(nèi)存的動態(tài)分配,并且分配的內(nèi)存應(yīng)該大于結(jié)構(gòu)的大小。sizeof(type)只有4. type *p= (type*)malloc(sizeof(type)+100*sizeof(int)); p->a[n] 方便的訪問數(shù)組。 在講述柔性數(shù)組成員之前,首先要介紹一下不完整類型(incomplete type)。不完整類型是這樣一種類型,它缺乏足夠的信息例如長度去描述一個完整的對象。6.2.5 Typesincomplete types (types that describe objects but lack information needed to determine their sizes).C與C++關(guān)于不完整類型的語義是一樣的。基本上沒有什么書介紹過不完整類型,很多人初次遇到這個概念時腦袋會一片空白。事實(shí)上我們在實(shí)際的工程設(shè)計中經(jīng)常使用不完整類型,只不過不知道有這么個概念而已。前向聲明就是一種常用的不完整類型:class base;struct test;base和test只給出了聲明,沒有給出定義。不完整類型必須通過某種方式補(bǔ)充完整,才能使用它們進(jìn)行實(shí)例化,否則只能用于定義指針或引用,因為此時實(shí)例化的是指針或引用本身,不是base或test對象。一個未知長度的數(shù)組也屬于不完整類型:extern int a[];extern不能去掉,因為數(shù)組的長度未知,不能作為定義出現(xiàn)。不完整類型的數(shù)組可以通過幾種方式補(bǔ)充完整才能使用,大括號形式的初始化就是其中一種方式:int a[] = { 10, 20 };柔性數(shù)組成員(flexible array member)也叫伸縮性數(shù)組成員,它的出現(xiàn)反映了C程序員對精煉代碼的極致追求。這種代碼結(jié)構(gòu)產(chǎn)生于對動態(tài)結(jié)構(gòu)體的需求。在日常的編程中,有時候需要在結(jié)構(gòu)體中存放一個長度動態(tài)的字符串,一般的做法,是在結(jié)構(gòu)體中定義一個指針成員,這個指針成員指向該字符串所在的動態(tài)內(nèi)存空間,例如: struct test {int a;double b;char *p; };p指向字符串。這種方法造成字符串與結(jié)構(gòu)體是分離的,不利于操作,如果把字符串跟結(jié)構(gòu)體直接連在一起,不是更好嗎?于是,可以把代碼修改為這樣:char a[] = “hello world”; struct test *PntTest = ( struct test* )malloc( sizeof( struct test ) + strlen( a ) + 1 ); strcpy( PntTest + 1, a );這樣一來,( char* )( PntTest + 1 )就是字符串“hello world”的地址了。這時候p成了多余的東西,可以去掉。但是,又產(chǎn)生了另外一個問題:老是使用( char* )( PntTest + 1 )不方便。如果能夠找出一種方法,既能直接引用該字符串,又不占用結(jié)構(gòu)體的空間,就完美了,符合這種條件的代碼結(jié)構(gòu)應(yīng)該是一個非對象的符號地址,在結(jié)構(gòu)體的尾部放置一個0長度的數(shù)組是一個絕妙的解決方案。不過,C/C++標(biāo)準(zhǔn)規(guī)定不能定義長度為0的數(shù)組,因此,有些編譯器就把0長度的數(shù)組成員作為自己的非標(biāo)準(zhǔn)擴(kuò)展,例如:struct test {int a;double b;char c[0]; };c就叫柔性數(shù)組成員,如果把PntTest指向的動態(tài)分配內(nèi)存看作一個整體,c就是一個長度可以動態(tài)變化的結(jié)構(gòu)體成員,柔性一詞來源于此。c的長度為0,因此它不占用test的空間,同時PntTest->c就是“hello world”的首地址,不需要再使用( char* )( PntTest + 1 )這么丑陋的語法了。鑒于這種代碼結(jié)構(gòu)所產(chǎn)生的重要作用,C99甚至把它收入了標(biāo)準(zhǔn)中:6.7.2.1 Structure and union specifiersAs a special case, the last element of a structure with more than one named member may have an incomplete array type; this is called a flexible array member.C99使用不完整類型實(shí)現(xiàn)柔性數(shù)組成員,標(biāo)準(zhǔn)形式是這樣的:struct test {int a;double b;char c[]; };c同樣不占用test的空間,只作為一個符號地址存在,而且必須是結(jié)構(gòu)體的最后一個成員。柔性數(shù)組成員不僅可以用于字符數(shù)組,還可以是元素為其它類型的數(shù)組,例如:struct test {int a;double b;float c[]; };應(yīng)當(dāng)盡量使用標(biāo)準(zhǔn)形式,在非C99的場合,可以使用指針方法。有些人使用char a[1],這是非常不可取的,把這樣的a用作柔性數(shù)組成員會發(fā)生越界行為,雖然C/C++標(biāo)準(zhǔn)并沒有規(guī)定編譯器應(yīng)當(dāng)檢查越界,但也沒有規(guī)定不能檢查越界,為了一個小小的指針空間而犧牲移植性,是不值得的。 簡化版應(yīng)用場景:有一串不同類型,不同大小的數(shù)據(jù)需要傳輸,某通信協(xié)議支持x byte的數(shù)據(jù)包長度,將這一串?dāng)?shù)據(jù)封裝成不大于x byte的數(shù)據(jù)包依次傳輸,如何設(shè)計數(shù)據(jù)包的封裝和解析?設(shè)計數(shù)據(jù)包:為了方便對端解析,設(shè)計數(shù)據(jù)包有頭部和數(shù)據(jù)兩段,頭部用結(jié)構(gòu)體的元素表示,需要什么信息則添加一個元素定義,數(shù)據(jù)段用柔性數(shù)組,是為了保證頭部和數(shù)據(jù)段得內(nèi)存保持連續(xù),如下所示: [html] view plaincopyprint? typedef struct node_s { int type; /* 頭部,可根據(jù)實(shí)際情況添加元素 */ int len; /* 頭部,標(biāo)識數(shù)據(jù)段已使用的長度 */ char value[0]; /* 數(shù)據(jù)段,柔性數(shù)組 */ } node_t; 所以,柔性數(shù)組實(shí)現(xiàn)擴(kuò)展長度的結(jié)構(gòu)體一句話概括就是:在結(jié)構(gòu)體的最后加上一個長度為零的char數(shù)組。用sizeof(node_t)可以發(fā)現(xiàn),柔性數(shù)組是不占內(nèi)存空間的。如何添加一段數(shù)據(jù)到數(shù)據(jù)包呢?1,初始化:[cpp] view plaincopyprint? #define MAX_INFO_SIZE (x - sizeof(node_t)) [cpp] view plaincopyprint? node_t *nodeinfo; char *mark; node_info = (node_t *)malloc(sizeof(node_t) + MAX_INFO_SIZE); if (node_info == NULL) { mem_error(); } [cpp] view plaincopyprint? memset(node_info, 0, sizeof(node_t) + MAX_INFO_SIZE); node_info->type = your_type; node_info->len = 0; mark = &node_info->value; 即是:申請一段內(nèi)存,初始化,將數(shù)據(jù)段的標(biāo)志位mark指向value地址,還沒有添加數(shù)據(jù),所以len為0;2,添加數(shù)據(jù)[cpp] view plaincopyprint? for (data = x; data < y; data = next(x)) { <span style="white-space:pre"> </span>/* 遍歷那串?dāng)?shù)據(jù),簡化版 */ len = get_data_len(data); if (node_info->len + len <= MAX_INFO_SIZE) { memcpy(mark, data, len); mark += len; node_info->len += len; } else { send(node_info); <span style="white-space:pre"> </span>/* 調(diào)用某協(xié)議發(fā)送出數(shù)據(jù),簡化版 */ memset(node_info, 0, sizeof(node_t) + MAX_INFO_SIZE); node_info->type = your_type; node_info->len = 0; mark = &node_info->value; memcpy(mark, data, len); mark += len; node_info->len += len; } } send(node_info); <span style="white-space:pre"> </span>/* 發(fā)送最后一個數(shù)據(jù)包 */ 添加數(shù)據(jù)的操作即是:將數(shù)據(jù)拷貝到數(shù)據(jù)段,記錄添加的長度,移動標(biāo)記位以便下次添加。如果數(shù)據(jù)達(dá)到數(shù)據(jù)包最大值,發(fā)送掉這個包,然后繼續(xù)添加操作。這樣,每個不大于x byte的數(shù)據(jù)包都填充好了data數(shù)據(jù),并且利用通信機(jī)制發(fā)送到了另一個地方。那“另一個地方”接收到了這個不大于x byte的數(shù)據(jù)包,如何從數(shù)據(jù)包里解析出data數(shù)據(jù)來呢?3,解析數(shù)據(jù)假設(shè)某臺機(jī)器得到了這個數(shù)據(jù)包,要查看數(shù)據(jù)包里每個data的內(nèi)容,如何解析?[cpp] view plaincopyprint? node_t *node_info; char *buf; int len; node_t *node_info; get_info(node_info); /* 讓node_info得到了那個不大于x byte的數(shù)據(jù)包,簡化版 */ for (buf = node_info->value; buf < node_info->value + node_info->len; buf += len) { data = (data_t *)buf; /* 從數(shù)據(jù)包里取到了一個data */ op(data); /* 查看數(shù)據(jù)包,簡化版 */ len = get_data_len(data); } 以上,利用柔性數(shù)組構(gòu)造一個數(shù)據(jù)包,并進(jìn)行封包,發(fā)包,解包的操作就完成了!在實(shí)際生產(chǎn)應(yīng)用中,要對數(shù)據(jù)包的頭部擴(kuò)展,其他的每一個操作都不會像例子的偽代碼那樣簡單,但原理是一樣的。如果沒有用過柔性數(shù)組的童鞋,可以思考一下:為什么最后一個原始是0元素的數(shù)組而不是指針呢?假如不是C99怎么辦? c99之 柔性數(shù)組成員時間:2012-09-07 13:18來源:Internet 作者:Internet 點(diǎn)擊:次在講述柔性數(shù)組成員之前,首先要介紹一下不完整類型(incomplete type)。不完整類型是這樣一種類型,它缺乏足夠的信息例如長度去描述一個完整的對象。 6.2.5 Types inco 在講述柔性數(shù)組成員之前,首先要介紹一下不完整類型(incomplete type)。不完整類型是這樣一種類型,它缺乏足夠的信息例如長度去描述一個完整的對象。6.2.5 Typesincomplete types (types that describe objects but lack information needed to determine their sizes).C與C++關(guān)于不完整類型的語義是一樣的。前向聲明就是一種常用的不完整類型:class base;struct test;base和test只給出了聲明,沒有給出定義。不完整類型必須通過某種方式補(bǔ)充完整,才能使用它們進(jìn)行實(shí)例化,否則只能用于定義指針或引用,因為此時實(shí)例化的是指針或引用本身,不是base或test對象。一個未知長度的數(shù)組也屬于不完整類型:extern int a[];extern不能去掉,因為數(shù)組的長度未知,不能作為定義出現(xiàn)。不完整類型的數(shù)組可以通過幾種方式補(bǔ)充完整才能使用,大括號形式的初始化就是其中一種方式:int a[] = { 10, 20 };柔性數(shù)組成員(flexible array member)也叫伸縮性數(shù)組成員,它的出現(xiàn)反映了C程序員對精煉代碼的極致追求。這種代碼結(jié)構(gòu)產(chǎn)生于對動態(tài)結(jié)構(gòu)體的需求。在日常的編程中,有時候需要在結(jié)構(gòu)體中存放一個長度動態(tài)的字符串,一般的做法,是在結(jié)構(gòu)體中定義一個指針成員,這個指針成員指向該字符串所在的動態(tài)內(nèi)存空間,例如:struct test{int a;double b;char *p;};p指向字符串。這種方法造成字符串與結(jié)構(gòu)體是分離的,不利于操作,如果把字符串跟結(jié)構(gòu)體直接連在一起,不是更好嗎?于是,可以把代碼修改為這樣:char a[] = “hello world”;struct test *PntTest = ( struct test* )malloc( sizeof( struct test ) + strlen( a ) + 1 );strcpy( PntTest + 1, a );這樣一來,( char* )( PntTest + 1 )就是字符串“hello world”的地址了(已經(jīng)忽略p)。這時候p成了多余的東西,可以去掉。但是,又產(chǎn)生了另外一個問題:老是使用( char* )( PntTest + 1 )不方便。如果能夠找出一種方法,既能直接引用該字符串,又不占用結(jié)構(gòu)體的空間,就完美了,符合這種條件的代碼結(jié)構(gòu)應(yīng)該是一個非對象的符號地址,在結(jié)構(gòu)體的尾部放置一個0長度的數(shù)組是一個絕妙的解決方案。不過,C/C++標(biāo)準(zhǔn)規(guī)定不能定義長度為0的數(shù)組,因此,有些編譯器就把0長度的數(shù)組成員作為自己的非標(biāo)準(zhǔn)擴(kuò)展,例如:struct test{int a;double b;char c[0];};c就叫柔性數(shù)組成員,如果把PntTest指向的動態(tài)分配內(nèi)存看作一個整體,c就是一個長度可以動態(tài)變化的結(jié)構(gòu)體成員,柔性一詞來源于此。c的長度為0,因此它不占用test的空間,同時PntTest->c就是“hello world”的首地址,不需要再使用( char* )( PntTest + 1 )這么丑陋的語法了。鑒于這種代碼結(jié)構(gòu)所產(chǎn)生的重要作用,C99甚至把它收入了標(biāo)準(zhǔn)中:6.7.2.1 Structure and union specifiersAs a special case, the last element of a structure with more than one named member may have an incomplete array type; this is called a flexible array member.C99使用不完整類型實(shí)現(xiàn)柔性數(shù)組成員,標(biāo)準(zhǔn)形式是這樣的:struct test{int a;double b;char c[];};c同樣不占用test的空間,只作為一個符號地址存在,而且必須是結(jié)構(gòu)體的最后一個成員。柔性數(shù)組成員不僅可以用于字符數(shù)組,還可以是元素為其它類型的數(shù)組,例如:struct test{int a;double b;float c[];};應(yīng)當(dāng)盡量使用標(biāo)準(zhǔn)形式,在非C99的場合,可以使用指針方法。有些人使用char a[1],這是非常不可取的,把這樣的a用作柔性數(shù)組成員會發(fā)生越界行為,雖然C/C++標(biāo)準(zhǔn)并沒有規(guī)定編譯器應(yīng)當(dāng)檢查越界,但也沒有規(guī)定不能檢查越界,為了一個小小的指針空間而犧牲移植性,是不值得的。柔性數(shù)組到底如何使用呢?看下面例子:typedef struct st_type{int i;int a[0];}type_a;有些編譯器會報錯無法編譯可以改成:typedef struct st_type{int i;int a[];}type_a;這樣我們就可以定義一個可變長的結(jié)構(gòu)體,用 sizeof(type_a)得到的只有 4,就是 sizeof(i)=sizeof(int)。那個 0 個元素的數(shù)組沒有占用空間,而后我們可以進(jìn)行變長操作了。通 過如下表達(dá)式給結(jié)構(gòu)體分配內(nèi)存: type_a *p = (type_a*)malloc(sizeof(type_a)+100*sizeof(int));這樣我們?yōu)榻Y(jié)構(gòu)體指針 p 分配了一塊內(nèi)存。用 p->item[n]就能簡單地訪問可變長元素。 但是這時候我們再用 sizeof(*p)測試結(jié)構(gòu)體的大小,發(fā)現(xiàn)仍然為 4。是不是很詭異?我們 不是給這個數(shù)組分配了空間么? 別急,先回憶一下我們前面講過的“模子” 。在定義這個結(jié)構(gòu)體的時候,模子的大小就 已經(jīng)確定不包含柔性數(shù)組的內(nèi)存大小。柔性數(shù)組只是編外人員,不占結(jié)構(gòu)體的編制。只是說 在使用柔性數(shù)組時需要把它當(dāng)作結(jié)構(gòu)體的一個成員,僅此而已。再說白點(diǎn),柔性數(shù)組其實(shí)與 結(jié)構(gòu)體沒什么關(guān)系,只是“掛羊頭賣狗肉”而已,算不得結(jié)構(gòu)體的正式成員。 需要說明的是:C89不支持這種東西,C99把它作為一種特例加入了標(biāo)準(zhǔn)。但是,C99 所支持的是 incomplete type,而不是 zero array,形同 int item[0];這種形式是非法的,C99支持的形式是形同 int item[];只不過有些編譯器把 int item[0];作為非標(biāo)準(zhǔn)擴(kuò)展來支持,而且在C99發(fā)布之前已經(jīng)有了這種非標(biāo)準(zhǔn)擴(kuò)展了,C99發(fā)布之后,有些編譯器把兩者合而為一了。當(dāng)然,上面既然用 malloc函數(shù)分配了內(nèi)存,肯定就需要用 free函數(shù)來釋放內(nèi)存: free(p); 經(jīng)過上面的講解,相信你已經(jīng)掌握了這個看起來似乎很神秘的東西。不過實(shí)在要是沒 掌握也無所謂,這個東西實(shí)在很少用。【柔性數(shù)組結(jié)構(gòu)成員C99中,結(jié)構(gòu)中的最后一個元素允許是未知大小的數(shù)組,這就叫做柔性數(shù)組成員,但結(jié)構(gòu)中的柔性數(shù)組成員前面必須至少一個其他成員。柔性數(shù)組成員允許結(jié)構(gòu)中包含一個大小可變的數(shù)組。sizeof返回的這種結(jié)構(gòu)大小不包括柔性數(shù)組的內(nèi)存。包含柔性數(shù)組成員的結(jié)構(gòu)用malloc ()函數(shù)進(jìn)行內(nèi)存的動態(tài)分配,并且分配的內(nèi)存應(yīng)該大于結(jié)構(gòu)的大小,以適應(yīng)柔性數(shù)組的預(yù)期大小。】C語言版:type_a *p = (type_a*)malloc(sizeof(type_a)+100*sizeof(int));C++語言版:type_a *p = (type_a*)new char[sizeof(type_a)+100*sizeof(int)];而釋放同樣簡單:C語言版:free(p);C++語言版:delete []p;?
總結(jié)
- 上一篇: 成语小秀才1154关答案
- 下一篇: LOL云顶之弈鸟盾怎么合成 lol直播云