程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C >> 關於C >> C語言柔性數組

C語言柔性數組

編輯:關於C
 結構中最後一個元素允許是未知大小的數組,這個數組就是柔性數組。但結構中的柔性數組前面必須至少一個其他成員,柔性數組成員允許結構中包含一個大小可變的數組,sizeof返回的這種結構大小不包括柔性數組的內存。包含柔數組成員的結構用malloc函數進行內存的動態分配,且分配的內存應該大於結構的大小以適應柔性數組的預期大小。柔性數組到底如何使用?   不完整類型 C和C++對於不完整類型的定義是一樣的,不完整類型是這樣一種類型,它缺乏足夠的信息例如長度去描述一個完整的對象。   不完整類型舉例: 前向聲明就是一種常用的不完整類型   struct test; //test 只給出了聲明,沒有給出定義 不完整數據類型必須通過某種方式補充完整,才能使它們進行實例化。否則只能用於定義指針或引用,因為此時實例化的是指針或引用本身,不是base和test對象   一個未知長度的數組也屬於不完整類型:   extern int a[]; extern 關鍵字不能去掉,因為數組的長度未知,不能作為定義出現。不完整類型的數組需要補充完整才能使用。不完整類型的數組可以通過幾種方式補充完整,大括號形式的初始化就是其中的一種方式:   int a[] = { 10,20 }; 結構體 首先,我們需要知道——所謂變量,其實是內存地址的一個抽像名字罷了。在靜態編譯的程序中,所有的變量名都會在編譯時被轉成內存地址。機器是不知道我們取的名字的,只知道地址。   所以有了——棧內存區,堆內存區,靜態內存區,常量內存區,我們代碼中的所有變量都會被編譯器預先放到這些內存區中。   有了上面這個基礎,我們來看一下結構體中的成員的地址是什麼?我們先簡單化一下代碼:   struct test{     int i;     char *p; }; 上面代碼中,test結構中i和p指針,在C的編譯器中保存的是相對地址——也就是說,他們的地址是相對於struct test的實例的。如果我們有這樣的代碼:   struct test t; 下面做個實驗:   #include<stdio.h> struct test{     int i;     char *p; }; int main(void) {     struct test t;     printf("%p\n", &t);     printf("%p\n", &(t.i));     printf("%p\n", &(t.p));     return 0; } 運行結果:       我們可以看到,t.i的地址和t的地址是一樣的,t.p的址址相對於t的地址多了個8。說白了,t.i 其實就是(&t + 0×0), t.p 的其實就是 (&t + 0×8)。0×0和0×8這個偏移地址就是成員i和p在編譯時就被編譯器給hard code了的地址。於是,你就知道,不管結構體的實例是什麼——訪問其成員其實就是加成員的偏移量。   下面再來做個實驗:   #include<stdio.h> struct test{     int i;     short c;     char *p; }; int main(void) {     struct test *pt=NULL;     printf("%p\n", &(pt->i));     printf("%p\n", &(pt->c));     printf("%p\n", &(pt->p));     return 0; } 運行結果:        注意:上面的pt->p的偏移之所以是0×8而不是0×6,是因為內存對齊了(我在64位系統上)。關於內存對齊,可參看《C語言內存對齊詳解》一文。   柔性數組   柔性數組成員(flexible array member)也叫伸縮性數組成員,這種代碼結構產生於對動態結構體的需求。在日常的編程中,有時候需要在結構體中存放一個長度動態的字符串,一般的做法,是在結構體中定義一個指針成員,這個指針成員指向該字符串所在的動態內存空間,例如:   struct s_test {     int a;     double b;     char* p; }; p指向字符串,這種方法造成字符串與結構體是分離的,不利於操作。把字符串和結構體連在一起的話,效果會更好,可以修改如下:   char a[] = "Hello world"; struct s_test *ptest = (struct s_test*)malloc(sizeof(s_test)+streln(a)+1); strcpy(ptest+1,a); 這樣一來,(char*)(ptestt + 1)就是字符串“hello world”的地址。這時候p成了多余的東西,可以去掉。但是,又產生了另外一個問題:老是使用(char*)(ptestt + 1)不方便。如果能夠找出一種方法,既能直接引用該字符串,又不占用結構體的空間,就完美了,符合這種條件的代碼結構應該是一個非對象的符號地址,在結構體的尾部放置一個0長度的數組是一個絕妙的解決方案。不過,C/C++標准規定不能定義長度為0的數組,因此,有些編譯器就把0長度的數組成員作為自己的非標准擴展,例如:   struct s_test2 {     int a;     double b;     char c[0]; }; c就叫柔性數組成員,如果把ptest指向的動態分配內存看作一個整體,c就是一個長度可以動態變化的結構體成員,柔性一詞來源於此。c的長度為0,因此它不占用test的空間,同時ptest->c就是“hello world”的首地址,不需要再使用(char*)(ptestt + 1)這麼丑陋的語法了。   鑒於這種代碼結構所產生的重要作用,C99甚至把它收入了標准中:   As 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使用不完整類型實現柔性數組成員,標准形式是這樣的:   struct s_test {   int a;   double b;   char c[]; }; c同樣不占用test的空間,只作為一個符號地址存在,而且必須是結構體的最後一個成員。柔性數組成員不僅可以用於字符數組,還可以是元素為其它類型的數組,例如:   struct s_test {     int a;     double b;     float[]; }; 首先,我們要知道,0長度的數組在ISO C和C++的規格說明書中是不允許的。這也就是為什麼在VC++2012下編譯你會得到一個警告:“arning C4200: 使用了非標准擴展 : 結構/聯合中的零大小數組”。   那麼為什麼gcc可以通過而連一個警告都沒有?那是因為gcc 為了預先支持C99的這種玩法,所以,讓“零長度數組”這種玩法合法了。關於GCC對於這個事的文檔在這裡:“Arrays of Length Zero”,文檔中給了一個例子,完整代碼如下:   #include <stdlib.h> #include <string.h> struct line {    int length;    char contents[0]; // C99的玩法是:char contents[]; 沒有指定數組長度 }; int main(){     int this_length=10;     struct line *thisline = (struct line *)                      malloc (sizeof (struct line) + this_length);     thisline->length = this_length;     memset(thisline->contents, 'a', this_length);     return 0; } 上面這段代碼的意思是:我想分配一個不定長的數組,於是我有一個結構體,其中有兩個成員,一個是length,代表數組的長度,一個是contents,代碼數組的內容。後面代碼裡的 this_length(長度是10)代表是想分配的數據的長度。
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved