gcc 0長數組學習

首先,咱們要知道,0長度的數組在ISO C和C++的規格說明書中是不容許的。這也就是爲何在VC++2012下編譯你會獲得一個警告:「warning C4200: 使用了非標準擴展 : 結構/聯合中的零大小數組」。html

那麼爲何gcc能夠經過而連一個警告都沒有?那是由於gcc 爲了預先支持C99的這種玩法,因此,讓「零長度數組」這種玩法合法了。關於GCC對於這個事的文檔在這裏:「Arrays of Length Zero」,文檔中給了一個例子(我改了一下,改爲能夠運行的了):shell

#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)表明是我想分配的數據的長度。(這看上去是否是像一個C++的類?)這種玩法英文叫:Flexible Array,中文翻譯叫:柔性數組。編程

咱們來用gdb看一下:數組

(gdb) p thisline
$1 = (struct line *) 0x601010
 
(gdb) p *thisline
$2 = {length = 10, contents = 0x601010 "\n"}
 
(gdb) p thisline->contents
$3 = 0x601014 "aaaaaaaaaa"

 

咱們能夠看到:在輸出*thisline時,咱們發現其中的成員變量contents的地址竟然和thisline是同樣的(偏移量爲0×0??!!)。可是當咱們輸出thisline->contents的時候,你又發現contents的地址是被offset了0×4了的,內容也變成了10個‘a’。(我以爲這是一個GDB的bug,VC++的調試器就能很好的顯示)bash

咱們繼續,若是你sizeof(char[0])或是 sizeof(int[0]) 之類的零長度數組,你會發現sizeof返回了0,這就是說,零長度的數組是存在於結構體內的,可是不佔結構體的size。你能夠簡單的理解爲一個沒有內容的佔位標識,直到咱們給結構體分配了內存,這個佔位標識才變成了一個有長度的數組。函數

看到這裏,你會說,爲何要這樣搞啊,把contents聲明成一個指針,而後爲它再分配一下內存不行麼?就像下面同樣。佈局

struct line {
   int length;
   char *contents;
};
 
int main(){
    int this_length=10;
    struct line *thisline = (struct line *)malloc (sizeof (struct line));
    thisline->contents = (char*) malloc( sizeof(char) * this_length );
    thisline->length = this_length;
    memset(thisline->contents, 'a', this_length);
    return 0;
}

 

這不同清楚嗎?並且也沒什麼怪異難懂的東西。是的,這也是廣泛的編程方式,代碼是很清晰,也讓人很容易理解。即然這樣,那爲何要搞一個零長度的數組?有毛意義?!this

這個事情出來的緣由是——咱們想給一個結構體內的數據分配一個連續的內存!這樣作的意義有兩個好處:spa

第一個意義是,方便內存釋放。若是咱們的代碼是在一個給別人用的函數中,你在裏面作了二次內存分配,並把整個結構體返回給用戶。用戶調用free能夠釋放結構體,可是用戶並不知道這個結構體內的成員也須要free,因此你不能期望用戶來發現這個事。因此,若是咱們把結構體的內存以及其成員要的內存一次性分配好了,並返回給用戶一個結構體指針,用戶作一次free就能夠把全部的內存也給釋放掉。(讀到這裏,你必定會以爲C++的封閉中的析構函數會讓這事容易和乾淨不少)翻譯

第二個緣由是,這樣有利於訪問速度。連續的內存有益於提升訪問速度,也有益於減小內存碎片。(其實,我我的以爲也沒多高了,反正你跑不了要用作偏移量的加法來尋址)

咱們來看看是怎麼個連續的,用gdb的x命令來查看:(咱們知道,用struct line {}中的那個char contents[]不佔用結構體的內存,因此,struct line就只有一個int成員,4個字節,而咱們還要爲contents[]分配10個字節長度,因此,一共是14個字節)

(gdb) x /14b thisline
0x601010:       10      0       0       0       97      97      97      97
0x601018:       97      97      97      97      97      97

 

從上面的內存佈局咱們能夠看到,前4個字節是 int length,後10個字節就是char contents[]。

若是用指針的話,會變成這個樣子:

(gdb) x /16b thisline
0x601010:       1       0       0       0       0       0       0       0
0x601018:       32      16      96      0       0       0       0       0
(gdb) x /10b this->contents
0x601020:       97      97      97      97      97      97      97      97
0x601028:       97      97

 

上面一共輸出了四行內存,其中,

  • 第一行前四個字節是 int length,第一行的後四個字節是對齊。
  • 第二行是char* contents,64位系統指針8個長度,他的值是0×20 0×10 0×60 也就是0×601020。
  • 第三行和第四行是char* contents指向的內容。

從這裏,咱們看到,其中的差異——數組的原地就是內容,而指針的那裏保存的是內容的地址

 

注:該文轉自酷客,選取了其中的關於0長數組的部分,之前0長數組也見過,可是爲何要用呢,有什麼好處呢,經過該文應該有一個瞭解!

相關文章
相關標籤/搜索