前兩天看程序,發現在某個函數中有下面這段程序:
int n; //define a variable n int array[n]; //define an array with length n
在我所學的C語言知識中,這種數組的定義在編譯時就應該有問題的,因為定義數組時,數組的長度必須要是一個大於0的整型字面值或定義為 const 的常量。例如下面這樣
int array1[10]; //valid int const N = 10; int array2[N]; //valid int n = 10; int array3[n]; //invalid
但從上面看第三種定義數組的方法也是正確的,於是,我用 gcc 去編譯這段程序,發現確實沒報錯,而且我對此數組進行一些操作,結果也都是正確!這簡直顛覆了我的知識框架!難道大學老師教我的、我平時看的書,都是錯誤的嗎?!我開始尋找答案...
最官方的解釋應該是 C 語言的規范和編譯器的規范說明了。
這下,終於安心了,原來這種語法確實是 C 語言規范,GCC 非常完美的支持了 ISO C99。但令人遺憾的是,我們的大學老師教給我們的還是老一套,雖然關系不是很大,但這也從側面反映了我們的教育是多麼地滯後!而且我們讀的 C 語言書,在不加任何限定的條件下,就說某某語法是不對的,讀書的人只能很痛苦地記下!小小吐槽一下,下面繼續...
這種變長數組有什麼好處呢?你可以使用 alloca
函數達到類似的動態分配數組的效果,但 alloca 函數分配的空間在函數退出時還依然存在,你需要手動地去釋放所分配的空間;VLA 就不一樣了,在數組名生命周期結束之後,所分配的空間也就隨之釋放。
當然,關於 VLA 還有很多限制,例如 ISO/IEC9899 給出了下面這個例子:
extern int n; int A[n]; // invalid: file scope VLA extern int (*p2)[n]; // invalid: file scope VM int B[100]; // valid: file scope but not VM void fvla(int m, int C[m][m]); // valid: VLA with prototype scope void fvla(int m, int C[m][m]) // valid: adjusted to auto pointer to VLA { typedef int VLA[m][m]; // valid: block scope typedef VLA struct tag { int (*y)[n]; // invalid: y not ordinary identifier int z[n]; // invalid: z not ordinary identifier }; int D[m]; // valid: auto VLA static int E[m]; // invalid: static block scope VLA extern int F[m]; // invalid: F has linkage and is VLA int (*s)[m]; // valid: auto pointer to VLA extern int (*r)[m]; // invalid: r has linkage and points to VLA static int (*q)[m] = &B; // valid: q is a static block pointer to VLA }
至於上面語法的原因,請參考 ISO/IEC9899 。
GCC 中允許使用零長數組,把它作為結構體的最後一個元素非常有用,下面例子出自 gcc 官方文檔。
struct line { int length; char contents[0]; }; struct line *thisline = (struct line *) malloc (sizeof (struct line) + this_length); thisline->length = this_length;
從上例就可以看出,零長數組在有固定頭部的可變對象上非常適用,我們可以根據對象的大小動態地去分配結構體的大小。
在 Linux 內核中也有這種應用,例如由於 PID 命名空間的存在,每個進程 PID 需要映射到所有能看到其的命名空間上,但該進程所在的命名空間在開始並不確定(但至少為 init 命名空間),需要在運行是根據 level 的值來確定,所以在該結構體後面增加了一個長度為 1 的數組(因為至少在一個init命名空間上),使得該結構體 pid 是個可變長的結構體,在運行時根據進程所處的命名空間的 level 來決定 numbers 分配多大。(注:雖然不是零長度的數組,但用法是一樣的)
struct pid { atomic_t count; unsigned int level; /* lists of tasks that use this pid */ struct hlist_head tasks[PIDTYPE_MAX]; struct rcu_head rcu; struct upid numbers[1]; };
Ubuntu 12.04嵌入式交叉編譯環境arm-linux-GCC搭建過程圖解 http://www.linuxidc.com/Linux/2013-06/85902.htm
Ubuntu 12.10安裝交叉編譯器arm-none-linux-gnueabi-GCC http://www.linuxidc.com/Linux/2013-03/82016.htm
Ubuntu下Vim+GCC+GDB安裝及使用 http://www.linuxidc.com/Linux/2013-01/78159.htm
Ubuntu下兩個GCC版本切換 http://www.linuxidc.com/Linux/2012-10/72284.htm
GCC 的詳細介紹:請點這裡
GCC 的下載地址:請點這裡