顧名思義,零長度陣列就是長度為0的陣列。
ansi c 標準規定:定義乙個陣列時,陣列的長度必須是乙個常數,即陣列的長度在編譯的時候是確定的。在ansi c 中定義乙個陣列的方法如下:
int a[10];
c99 新標準規定:可以定義乙個變長陣列。
int len;
int a[len];
也就是說,陣列的長度在編譯時是未確定的,在程式執行的時候才確定,甚至可以由使用者指定大小。比如,我們可以定義乙個陣列,然後在程式執行時才指定這個陣列的大小,還可以通過輸入資料來初始化陣列,示例**如下。
int main(void)
在這個程式中,我們定義乙個零長度陣列,使用 sizeof 檢視其大小可以看到:零長度陣列在記憶體中不占用空間,大小為0。
零長度陣列一般單獨使用的機會很少,它常常作為結構體的乙個成員,構成乙個變長結構體。
struct buffer;
int main(void)
零長度陣列在結構體中同樣不占用儲存空間,所以 buffer 結構體的大小為4。
零長度陣列經常以變長結構體的形式,在某些特殊的應用場合,被程式設計師使用。在乙個變長結構體中,零長度陣列不占用結構體的儲存空間,但是我們可以通過使用結構體的成員 a 去訪問記憶體,非常方便。變長結構體的使用示例如下。
struct buffer;
int main(void)
在這個程式中,我們使用 malloc 申請一片記憶體,大小為 sizeof(buffer) + 20,即24個位元組大小。其中4個位元組用來儲存結構體指標 buf 指向的結構體型別變數,另外20個位元組空間,才是我們真正使用的記憶體空間。我們可以通過結構體成員 a,直接訪問這片記憶體。
零長度陣列在核心中,一般以變長結構體的形式使用。今天我們就分析一下 linux 核心中的 usb 驅動。在網絡卡驅動中,大家可能都比較熟悉乙個名字:套接字緩衝區,即 socket buffer,用來傳輸網路資料報。同樣,在 usb 驅動中,也有乙個類似的東西,叫 urb,其全名為 usb request block,即 usb 請求塊,用來傳輸 usb 資料報。
struct urb ;
struct usb_iso_packet_descriptor iso_frame_desc[0];
大家在各種場合,可能常常會看到這樣的字眼:陣列名在作為函式引數進行引數傳遞時,就相當於是乙個指標。在這裡,我們千萬別被這句話迷惑了:陣列名在作為函式引數傳遞時,確實傳遞的是乙個位址,但陣列名絕不是指標,兩者不是同乙個東西。陣列名用來表徵一塊連續記憶體儲存空間的位址,而指標是乙個變數,編譯器要給它單獨再分配乙個記憶體空間,用來存放它指向的變數的位址。我們看下面這個程式。
struct buffer1;
struct buffer2;
int main(void)
執行結果分別為:
buffer1:4
buffer2:8
對於乙個指標變數,編譯器要為這個指標變數單獨分配乙個儲存空間,然後在這個儲存空間上存放另乙個變數的位址,我們就說這個指標指向這個變數。而陣列名,編譯器不會再給其分配乙個儲存空間的,它僅僅是乙個符號,跟函式名一樣,用來表示乙個位址。我們接下來看另乙個程式。
int array1[10] =;
int array2[0];
int *p = &array1[5];
int main(void)
在這個程式中,我們分別定義乙個普通陣列、乙個零長度陣列和乙個指標變數。其中這個指標變數 p 的值為 array1[5] 這個陣列元素的位址,也就是說指標 p 指向 arraay1[5]。我們接著對這個程式使用 arm 交叉編譯器進行編譯,並進行反彙編。
$ arm-linux-gnueabi-gcc hello.c -o a.out
$ arm-linux-gnueabi-objdump -d a.out
從反彙編生成的彙編**中,我們找到 array1 和指標變數 p 的彙編**。
00021024 :
21024: 00000001 andeq r0, r0, r1
21028: 00000002 andeq r0, r0, r2
2102c: 00000003 andeq r0, r0, r3
21030: 00000004 andeq r0, r0, r4
21034: 00000005 andeq r0, r0, r5
21038: 00000006 andeq r0, r0, r6
2103c: 00000007 andeq r0, r0, r7
21040: 00000008 andeq r0, r0, r8
21044: 00000009 andeq r0, r0, r9
21048: 00000000 andeq r0, r0, r0
0002104c :
2104c: 00021038 andeq r1, r2, r8, lsr r0
disassembly of section .bss:
00021050 <__bss_start>:
21050: 00000000 andeq r0, r0, r0
從彙編**中,可以看到,對於長度為10的陣列 array1[10],編譯器給它分配了從 0x21024--0x21048 一共40個位元組的儲存空間,但並沒有給陣列名 array1 單獨分配儲存空間,陣列名 array1 僅僅表示這40個連續儲存空間的首位址,即陣列元素 array1[0] 的位址。而對於 array2[0] 這個零長度陣列,編譯器並沒有給它分配儲存空間,此時的 array2 僅僅是乙個符號,用來表示記憶體中的某個位址,我們可以通過檢視可執行檔案 a.out 的符號表來找到這個位址值。
$ readelf -s a.out
88: 00021024 40 object global default 23 array1
89: 00021054 0 notype global default 24 _bss_end__
90: 00021050 0 notype global default 23 _edata
91: 0002104c 4 object global default 23 p
92: 00010480 0 func global default 14 _fini
93: 00021054 0 notype global default 24 __bss_end__
94: 0002101c 0 notype global default 23 __data_start_
96: 00000000 0 notype weak default und __gmon_start__
97: 00021020 0 object global hidden 23 __dso_handle
98: 00010488 4 object global default 15 _io_stdin_used
99: 0001041c 96 func global default 13 __libc_csu_init
100: 00021054 0 object global default 24 array2
101: 00021054 0 notype global default 24 _end
102: 000102d8 0 func global default 13 _start
103: 00021054 0 notype global default 24 __end__
104: 00021050 0 notype global default 24 __bss_start
105: 00010400 28 func global default 13 main
107: 00021050 0 object global hidden 23 __tmc_end__
110: 00010294 0 func global default 11 _init
從符號表裡可以看到,array2 的位址為 0x21054,在程式 bss 段的後面。array2 符號表示的預設位址是一片未使用的記憶體空間,僅此而已,編譯器絕不會單獨再給其分配乙個記憶體空間來儲存陣列名。看到這裡,也許你就明白了:陣列名和指標並不是一回事,陣列名雖然在作為函式引數時,可以當乙個位址使用,但是兩者不能劃等號。菜刀有時候可以當**用,但是你不能說菜刀就是**。
至於為什麼不用指標,很簡單。使用指標的話,指標本身也會占用儲存空間不說,根據上面的 usb 驅動的案例分析,你會發現,它遠遠沒有零長度陣列用得巧妙——不會對結構體定義造成冗餘,而且使用起來也很方便。
qq群:475504428
嵌入式C語言自我修養 14 10 道C語言筆試題
1.分析下面的c 它的執行結果可能是 include intmain void 2.關於變數的宣告和定義,下面說法錯誤的是 3.在下面對一些變數的宣告中,有可能是定義語句的是 4.閱讀下面的 可能的執行結果是 include intmain void 5.關於陣列指標的使用,下面哪一條賦值語句可能會...
c語言之零長度陣列
我們設想這樣乙個場景,我們在網路通訊過程中使用的資料緩衝區,緩衝區包括乙個len欄位和data欄位,分別標識資料的長度和傳輸的資料,我們常見的有幾種設計思路 1 定長資料緩衝區,設定乙個足夠大小 max length 的資料緩衝區 2 設定乙個指向實際資料的指標,每次使用時,按照資料的長度動態的開闢...
嵌入式學習(二) 嵌入式系統C 語言
1 從 cpu 復位時的指定位址開始執行 2 跳轉至彙編 startup 處執行 3 跳轉至使用者主程式 main 執行,在 main 中完成 a.初試化各硬體裝置 b.初始化各軟體模組 c.進入死迴圈 無限迴圈 d呼叫各模組的處理函式 下面是幾個 著名 的死迴圈 1 作業系統是死迴圈 2 win3...