C言語の0配列\フレキシブル配列の使用詳細


前言:
前回ある面接を見ましたが、中にはある友達が、面接官がchar[0]に関する質問をしましたが、自分が会ったことがないので、この問題を避けました。

私は自分でこの文章の下で返事をしました。

今は自分の理解と結びつけて、このchar[0]C言語の柔軟性配列の問題を説明したいです。
0配列とフレキシブル配列の紹介
0配列は名前の通り、つまり配列長が0と定義されています。配列長が少なくとも1であることを知っています。0を定義した配列は空間がないです。しかし、上の構造体のように最後のメンバでゼロ配列と定義すれば、ゼロ配列は割り当てられていない空間ですが、オフセット量として使用できます。配列名という記号自体が、変更できないアドレス定数を表しているからです。フレキシブル配列は伸縮性配列ともいいますが、0配列は柔軟性のある配列です。
初期に0長さ配列を導入していなかったため、長い配列と針を定める方式で解決されましたが、長い配列は十分大きいバッファを定義しています。このように使うのは便利です。しかし、毎回空間の無駄な指針を作る方式で、プログラム員に解放空間で何度もfree操作を行わなければなりません。私たちは使用中によく関数の中でバッファを指すポインタを返します。私たちは全員が私たちのリリース方式を理解していることを保証することはできません。だからGNUはそれを0長さの配列に拡張しました。data[0]を使うと、0長さの配列になります。0長さの配列は配列名として、記憶空間を占有しません。メモリをより効率的に利用することができます。
C 99の後にも似たような拡張が加えられていますが、char payloadという形に変えられます。もしコンパイルする時に-pedanticパラメータを使う必要があるなら、char payload[0]タイプをchar payload[]に変えてコンパイルしてもいいです。もちろんコンパイラはC 99規格をサポートしなければなりません。それは支持しないかもしれません。
0配列の全般的な使用
まず構造体を定義して,もう一つの構造体の最後に長さ0の配列を定義すれば,この構造体を可変長とすることができる。
以下の通りです

//  0    
struct zero_buffer
{
    int     len;
    char    data[0];
};
この時data[0]は配列名だけで、記憶空間を占用しません。
この構造体の大きさはsizeofで長さをとり、実際にはそのメンバーintの長さであり、data[0]は空間を占有しない。配列名は記号だけで、任意の空間を占有することはありません。構造体ではオフセット量を表しています。変更できないアドレス定数を表しています。)
sizeof(struct zero_)buffer)=sizeof(int)

printf("zero struct length is:%d int length is:%d
",sizeof(struct zero_buffer),sizeof(int));
ゼロstruct length is:4 int length is:4
この特徴は0長配列で、バッファ、パケットなどのデータ構造が必要です。
構造体定義は上記の通りです。
もしtcpで送信するデータを設定する必要があるとしたら、長さは15で、データの内容は「Hello My Friend」です。このように私達は下のように定義できます。そのうち  zbuffer->dataはデータを定義するアドレスであり、lenはデータの長さを表している。
スペースを開いてから使います。
私たちが使う時は、一回だけ空間を開けてもいいです。

#define CURR_LENGTH 15struct zero_buffer  *zbuffer = NULL;//    if ((zbuffer = (struct zero_buffer *)malloc(sizeof(struct zero_buffer) + sizeof(char) * CURR_LENGTH)) != NULL){    zbuffer->len = CURR_LENGTH;    memcpy(zbuffer->data, "Hello My Friend", CURR_LENGTH);    printf("%d, %s
", zbuffer->len, zbuffer->data);}
使用済みリリーススペース
スペースを一度にリリースすればいいです。

//    
free(zbuffer);
zero_buffer = NULL;
他の方法は、いくつかの不定長データの転送を実現する。
0配列の他に、固定長配列とポインタ配列を使ってフレキシブル配列を実現する機能があります。
定行列
定長配列というのは、構造体の中に定長の配列があります。この配列の大きさは私達が定義したデータによって最大に設定されています。データが蓄積される時にオーバーフローするのを防ぐためです。
定義

//       
#define MAX_LENGTH      512
struct max_buffer
{
    int     len;
    char    data[MAX_LENGTH];
};
しかし、使用中、例えば512バイトのデータを送りたいですが、もし長いカバンを使うなら、長いカバンの最大長さMAX_を想定しています。LENGTHは1024で、512バイトの空間を浪費し、無駄な流量をもたらす。配列構造が整っていれば(この知識は詳しくは私の前のデータの配置の文章を読むことができます)sizef(struct max_)buffer)=sizeof(int)+sizeof(char)*MAX_LENGTH
パケットの構造
一般的には、バッファリングを指すデータ構造のmax_を返します。ブザーの針

#define CURR_LENGTH 512struct max_buffer   *mbuffer = NULL;if ((mbuffer = (struct max_buffer *)malloc(sizeof(struct max_buffer))) != NULL){    mbuffer->len = CURR_LENGTH;    memcpy(mbuffer->data, "Hello World", CURR_LENGTH);    printf("%d, %s
", mbuffer->len, mbuffer->data);} : :https://juejin.cn/post/6960470520831672333 : 。 , 。
前の4バイトのp->lenは、ヘッダとして、このヘッダはパケットの後のデータ部分の長さを説明するために用いられます。ここは1024です。したがって、前の4バイトの割り当ては1024です。私たちはこのパケットの長さを変数によって示す必要があります。これがlenの役割です。
その後のメモリは本当のデータ部分で、p->dataを通して、最後に、一つのmemcpy()メモリコピーを行い、送信するデータをこのメモリに記入します。
リリーススペース
データをリリースするスペースを使用した後、そのままリリースすればいいです。

free(mbuffer);
mbuffer = NULL;
定長配列を使用して、データバッファとして、バッファオーバーフローを避けるために、配列のサイズを十分な空間MAX_に設定します。LENGTHは、実際に使用する過程で、MAX_に到達します。LENGTHの長さのデータはとても少なくて、そんなに多くの情況の下で、緩衝区の大部分の空間はすべて浪費してしまいました。
しかし、使用プロセスは簡単で、データ空間の開発とリリースは簡単で、プログラマが追加の操作を考慮する必要はありません。
ポインタ配列
ゼロ配列の最後の構造体要素はdata[0]を定義し、ポインタ配列は構造体の中で一つのポインタ配列を定義する必要があります。この中のポインタ配列は構造体の最後の要素を特定する必要がありません。

struct point_buffer
{
    char    *data;
    int     len;
};
配列の配置を考慮して(この知識は詳細には私の前の「データ配置」(https://www.jb51.net/article/211811.htm)の文章を見ることができます。データ構造の大きさはsizeofです。buffer)=sizeof(int)+(補整intとchar*タイプの長さ値)+sizeof(char*)は、私の64ビットコンパイル環境ではintタイプは4 byte、char*タイプは8 byteですので、補完の長さは8-4、最終sizeof(point of)です。16 byteです。
構造体に加えると  _atribute(packed)  データ配置の修飾は、sizeof(point_buffer)=sizeof(int)  sizeof(char*)は、最終的に12 byteと計算されます。
スペース割り当ての使用

#define CURR_LENGTH 1024 struct point_buffer *pbuffer = NULL;if ((pbuffer = (struct point_buffer *)malloc(sizeof(struct point_buffer))) != NULL){   pbuffer->len = CURR_LENGTH;   if ((pbuffer->data = (char *)malloc(sizeof(char) * CURR_LENGTH)) != NULL)   {       memcpy(pbuffer->data, "Hello World", CURR_LENGTH);       printf("%d, %s
", pbuffer->len, pbuffer->data);   }}
メモリを割り当てるには、2ステップが必要です。
まず、構造体にメモリ空間を割り当てる必要があります。
次に、構造体のメンバー変数にメモリ空間を割り当てる。
このように二回にわたって割り当てられたメモリは不連続であり、それぞれを管理する必要があります。長さが長い配列を使用する場合は、一度に割り当てる原則を採用して、必要なメモリを一度に全部割り当てます。
釈放する
逆に、リリース時も同じです。

free(pbuffer->data);
free(pbuffer);
pbuffer = NULL;
ポインタの結果をバッファとして使用して、針の大きさの空間だけを使用して、固定長の配列を使用する必要がなくて、空間の大量の浪費をもたらしません。
しかし、それは空間を開くには、データドメインの空間を追加的に開拓する必要があり、リリース時にはデータドメインの空間を表示する必要がありますが、実際の使用過程では、関数の中に空間を開き、ユーザーにstruct pointを指示します。バfferの指針は、この時に使用者が私達の開発の詳細を知っていると仮定することはできません。そして、約束の操作に従って空間を解放します。そのため、使うのにどれだけ不便かがあります。メモリが漏れてしまうこともあります。
リボン:
長い配列を決めて使うのは便利ですが、空間を浪費して、針の形は一つの指針の空間だけを多く使っています。大量の空間を浪費することはありません。だから一番いい解です
0配列の優劣と注意事項
利点:構造体にポインター変数を宣言し、動的に割り当てる方法よりも効率的です。配列の内容にアクセスする時、間接的な訪問は必要ないので、二回の訪問は避けられました。また、0配列も定长配列のようにメモリが无駄になることはありません。
欠点:構造体では、配列が0の配列は最後の宣言で使用される必要があります。
締め括りをつける
ここでC言語の0配列\フレキシブル配列についての記事を紹介します。C言語の0配列\フレキシブル配列の内容については以前の文章を検索してください。または次の関連記事を引き続き閲覧してください。これからもよろしくお願いします。