C語言柔性數組詳解
前言
可能大傢第一眼看到這個標題會有點懵,到底什麼是柔性數組,我怎麼從來沒聽說過?但柔性數組確實是存在的,也經常會出現在一些公司的面試題中,今天就跟著筆者來學習一下柔性數組吧。
提示:以下是本篇文章正文內容,下面案例可供參考
一、柔性數組是什麼?
C99中,結構體中的最後一個元素允許是未知大小的數組,這就叫作柔性數組,for example:
struct st_type { int i; int a[0];//柔性數組成員,也可以寫int a[]; };
結構體成員a數組,它的數組大小是沒有確定的,將來如果需要可以大也可以小。
有些編譯器支持a[0]這種寫法,有些編譯器支持a[ ]這種寫法,具體取決編譯器。
二、柔性數組的特點
1.結構體中柔性數組成員前面必須至少有一個其他成員
示例如下:
struct st_type { int i; int a[0];//柔性數組成員,也可以寫int a[]; };
比如上面這段代碼,如果你要創建一個柔性數組a,前面必須創建一些別的成員
2.sizeof返回的這種結構大小不包括柔性數組的內存
示例如下:
struct st_type { int i;//4字節 int a[0];//柔性數組成員,也可以寫int a[]; //因為是柔性數組,無法確認a占幾個字節 }; int main() { printf("%d\n", sizeof(struct st_type));//打印4 return 0; }
這裡計算包含柔性數組的結構體大小,因為柔性數組本身是無法確定有幾個字節的,所以計算整體結構體大小時,會省略柔性數組的計算。
3.包含柔性數組成員的結構用malloc()函數進行內存的動態分配,並且分配的內存應該大於結構的大小,以適應柔性數組的預期大小
ps:除瞭malloc函數,realloc、calloc等動態內存開辟的函數也需要類似的操作
比如說我現在要數組a裡面有10個元素,現在進行malloc一下
示例如下:
#include<string.h> #include<errno.h> struct st_type { int i;//4字節 int a[0];//柔性數組成員,也可以寫int a[]; }; int main() { //假設我現在需要a裡有10個元素 struct st_type*ps=(struct st_type*)malloc(sizeof(struct st_type) + 10 * sizeof(int)); if (ps == NULL)//由於空間可能不夠開辟導致malloc開辟失敗,開辟失敗會返回空指針 { printf("%s\n", strerror(errno)); return -1;//程序出問題後,跳出程序 } //開辟成功 int j = 0; for (j = 0;j < 10;j++) { ps->a[j] = j; } for (j = 0;j < 10;j++) { printf("%d ", ps->a[j]);//打印0-9 } printf("\n"); //如果想繼續用柔性數組a進行打印 //比如現在a裡隻有10個元素,我用完10個瞭,我還要繼續來10個,用realloc追加 struct st_type*ptr=realloc(ps, sizeof(struct st_type) + 20 * sizeof(int));//ps:realloc第二個參數是調整後的整體大小 if (ptr == NULL) { printf("擴容失敗\n"); return -1; } else { ps = ptr; } //擴容成功 int k = 0; for (k = 10;k < 20;k++) { ps->a[k] = k; } for (j = 0;j < 20;j++) { printf("%d ", ps->a[j]);//打印0-19 } //釋放空間 free(ps); ps = NULL; return 0; }
我們這裡需要數組a裡有10個元素,那我們malloc的時候要對結構體裡的整形i先開辟4個字節,然後為整形數組a再開辟40個字節,然後malloc函數返回開辟空間的起始地址,賦給truct st_type * 類型的ps指針。
malloc(sizeof(struct st_type) + 10 * sizeof(int))這個操作等價於struct st_type類型創建一個變量所占空間,隻不過是用malloc來開辟
你改變數組a大小,追加空間時,realloc(ps, sizeof(struct st_type) + 20 * sizeof(int)),realloc的第一個參數仍然是ps,因為你當時是用malloc一次開辟出的一塊空間,你是不能單獨調整數組a的空間的
三、柔性數組的優點
柔性數組就是對一塊空間實現動態開辟嘛,那我們之前也講過指針來動態內存開辟,我們來看一段代碼來對比一下這兩種方法:
//用指針也可以做到a指向的空間動態變化 struct st_type { int i;//4字節 int *a;//4字節,這裡計算結構體大小恰好是8字節 }; int main() { struct st_type*ps = (struct st_type*)malloc(sizeof(struct st_type)); ps->i = 100; ps->a = (int*)malloc(10 * sizeof(int));//a指向40個字節的空間,該空間由int*進行管理 int j = 0; for (j = 0;j < 10;j++) { ps->a[j] = j;//a[j]=*(a+j) } for (j = 0;j < 10;j++) { printf("%d", ps->a[j]); } //a指向的空間不夠瞭,希望調整大小 int *ptr = (int*)realloc(ps->a, 20 * sizeof(int)); if (ptr == NULL) { printf("擴容失敗"); return -1; } else { ps->a = ptr; } //使用... //釋放 free(ps->a); ps->a = NULL; free(ps); ps = NULL; }
這裡需要註意的是,在釋放空間時,你要先釋放指針a指向的空間,然後釋放結構體指針
如上圖,我們結構體指針ps開辟一塊空間,空間裡存放整形i和整形指針a,a又malloc(後續如果需要還可以realloc追加)一塊空間,如果你先釋放掉ps,a就沒瞭,你就沒法找到a指向的那塊空間瞭。
還是那個生動的例子 就比如a是一個警察頭子,malloc開辟的空間是臥底,隻有a知道那個臥底,你現在警察頭子死瞭,再也沒法證明臥底是臥底瞭,也就是說a消失後,沒辦法再對開辟的空間進行釋放,這時就會造成內存泄露,指針實現動態內存調整是需要對指針釋放講解一定的順序性的
這裡對比柔性數組,柔性數組和上述的指針都可以實現一塊空間大小的調整,但是柔性數組有兩個好處:
第一個好處是:方便內存釋放
如果我們的代碼是在一個給別人用的函數中,你在裡面做瞭二次內存分配,並把整個結構體返回給用戶。用戶調用free可以釋放結構體,但是用戶並不知道這個結構體內的成員也需要free,所以你不能指望用戶來發現這個事。以上,如果我把結構體的內存及其成員要的內存一次性分配好,並返回給用戶一個結構體指針,用戶做一次free就可以把所有內存都釋放掉,並且不用考慮前面說的釋放的順序。
第二個好處是:加快訪問速度
連續的內存有益於提高訪問速度,也有益於減少內存碎片。
ps:內存碎片如下圖
操作系統給我們一塊內存,我們在進行malloc時,不一定就是一塊連著一塊的,上圖的空白部分就是內存碎片,有些類似我們裁剪佈料,一些剩餘的邊角料這樣。
總結
本文介紹瞭柔性數組的定義、其三個使用特點,及其對比指針實現動態內存的優勢,並對其進行瞭具體舉例
本篇文章就到這裡瞭,希望能夠給你帶來幫助,也希望您能夠多多關註WalkonNet的更多內容!