歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

GCC 中零長數組與變長數組

前兩天看程序,發現在某個函數中有下面這段程序:

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 語言的規范和編譯器的規范說明了。

  • 在 ISO/IEC9899 標准的 6.7.5.2 Array declarators 中明確說明了數組的長度可以為變量的,稱為變長數組(VLA,variable length array)。(注:這裡的變長指的是數組的長度是在運行時才能決定,但一旦決定在數組的生命周期內就不會再變。
  • 在 GCC 標准規范的 6.19 Arrays of Variable Length 中指出,作為編譯器擴展,GCC 在 C90 模式和 C++ 編譯器下遵守 ISO C99 關於變長數組的規范。

這下,終於安心了,原來這種語法確實是 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 中允許使用零長數組,把它作為結構體的最後一個元素非常有用,下面例子出自 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];
};

參考資料

  • ISO/IEC9899
  • GCC Online Documents

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 的下載地址:請點這裡

Copyright © Linux教程網 All Rights Reserved