你學過數組,那你知道柔性數組嗎?

編程學習基地 2020-11-13程序員

如下文章來源於C語言與CPP編程 ,做者自成一派123算法

C語言與CPP編程C語言與CPP編程編程

分享C語言/C++,數據結構與算法,計算機基礎,操做系統等數組

1 引言

定長數組包

在平時的開發中,緩衝區數據收發時,若是採用緩衝區定長包,假定大小是 1k,MAX_LENGTH爲 1024。結構體以下:網絡

//  定長緩衝區
struct max_buffer
{
    int   len;
    char  data[MAX_LENGTH];
};

數據結構的大小 >= sizeof(int) + sizeof(char) * MAX_LENGTH爲了防止數據溢出的狀況,data 的長度通常會設置得足夠大,但也正是由於這樣,纔會致使數組的冗餘。數據結構

假如發送 512 字節的數據,  就會浪費 512 個字節的空間, 平時通訊時,大多數是心跳包,大小遠遠小於 1024,除了浪費空間還消耗不少流量。app

內存申請:ide

if ((m_buffer = (struct max_buffer *)malloc(sizeof(struct max_buffer))) != NULL)
{
    m_buffer->len = CUR_LENGTH;
    memcpy(m_buffer->data, "max_buffer test", CUR_LENGTH);
    printf("%d, %s\n", m_buffer->len, m_buffer->data);
}

內存釋放:學習

free(m_buffer);
m_buffer = NULL;

指針數據包

爲了不空間上的浪費,咱們能夠將上面的長度爲 MAX_LENGTH 的定長數組換爲指針, 每次使用時動態的開闢 CUR_LENGTH 大小的空間。數據包結構體定義:flex

struct point_buffer
{
    int   len;
    char  *data;
};

數據結構大小 >= sizeof(int) + sizeof(char *)但在內存分配時,須要兩步進行:

  • 需爲結構體分配一塊內存空間;
  • 爲結構體中的成員變量分配內存空間;

內存申請:

if ((p_buffer = (struct point_buffer *)malloc(sizeof(struct point_buffer))) != NULL)
{
    p_buffer->len = CUR_LENGTH;
    if ((p_buffer->data = (char *)malloc(sizeof(char) * CUR_LENGTH)) != NULL)
    {
        memcpy(p_buffer->data, "point_buffer test", CUR_LENGTH);
        printf("%d, %s\n", p_buffer->len, p_buffer->data);
    }
}

內存釋放:

free(p_buffer->data);
free(p_buffer);
p_buffer = NULL;

雖然這樣可以節約內存,可是兩次分配的內存是不連續的, 須要分別對其進行管理,致使的問題就是須要對結構體和數據分別申請和釋放內存,這樣對於程序員來講無疑是一個災難,由於這樣很容易致使遺忘釋放內存形成內存泄露。

有沒有更好的方法呢?那就是今天的主題柔性數組。

2 柔性數組

什麼是柔性數組?

柔性數組成員(flexible array member)也叫伸縮性數組成員,這種代碼結構產生於對動態結構體的需求。在平常的編程中,有時候須要在結構體中存放一個長度動態的字符串,鑑於這種代碼結構所產生的重要做用,C99 甚至把它收入了標準中:

As a special case, the last element of a structure with more than one named member may have an incomplete array type; this is called a flexible array member.

柔性數組是 C99 標準引入的特性,因此當你的編譯器提示不支持的語法時,請檢查你是否開啓了 C99 選項或更高的版本支持。

C99 標準的定義以下:

struct test {
    short len;  // 必須至少有一個其它成員
    char arr[]; // 柔性數組必須是結構體最後一個成員(也但是其它類型,如:int、double、...)
};
  • 柔性數組成員必須定義在結構體裏面且爲最後元素;
  • 結構體中不能單獨只有柔性數組成員;
  • 柔性數組不佔內存。

在一個結構體的最後,申明一個長度爲空的數組,就可使得這個結構體是可變長的。對於編譯器來講,此時長度爲 0 的數組並不佔用空間,由於數組名自己不佔空間,它只是一個偏移量,數組名這個符號自己表明了一個不可修改的地址常量,

但對於這個數組的大小,咱們能夠進行動態分配,對於編譯器而言,數組名僅僅是一個符號,它不會佔用任何空間,它在結構體中,只是表明了一個偏移量,表明一個不可修改的地址常量!

對於柔性數組的這個特色,很容易構造出變成結構體,如緩衝區,數據包等等, 其實柔性數組成員在實現跳躍表時有它特別的用法,在Redis的SDS數據結構中和跳躍表的實現上,也使用柔性數組成員。它的主要用途是爲了知足須要變長度的結構體,爲了解決使用數組時內存的冗餘和數組的越界問題

柔性數組解決引言的例子

//柔性數組
struct soft_buffer
{
    int   len;
    char  data[0];
};

數據結構大小 = sizeof(struct soft_buffer) = sizeof(int),這樣的變長數組經常使用於網絡通訊中構造不定長數據包, 不會浪費空間浪費網絡流量。

申請內存:

if ((softbuffer = (struct soft_buffer *)malloc(sizeof(struct soft_buffer) + sizeof(char) * CUR_LENGTH)) != NULL)
{
    softbuffer->len = CUR_LENGTH;
    memcpy(softbuffer->data, "softbuffer test", CUR_LENGTH);
    printf("%d, %s\n", softbuffer->len, softbuffer->data);
}

釋放內存:

free(softbuffer);
softbuffer = NULL;

對比使用指針和柔性數組會發現,使用柔性數組的優勢:

  • 因爲結構體使用指針地址不連續(兩次 malloc),柔性數組地址連續,只須要一次 malloc,一樣釋放前者須要兩次,後者能夠一塊兒釋放。
  • 在數據拷貝時,結構體使用指針時,必須拷貝它指向的內存,內存不連續會存在問題,柔性數組能夠直接拷貝。
  • 減小內存碎片,因爲結構體的柔性數組和結構體成員的地址是連續的,便可一同申請內存,所以更大程度地避免了內存碎片。另外因爲該成員自己不佔結構體空間,所以,總體而言,比普通的數組成員佔用空間要會稍微小點。

缺點:對結構體格式有要求,必要放在最後,不是惟一成員。

3 總結

在平常編程中,有時須要在結構體中存放一個長度是動態的字符串(也多是其餘數據類型),可使用柔性數組,柔性數組是一種可以巧妙地解決數組內存的冗餘和數組的越界問題一種方法。很是值得你們學習和借鑑。

相關文章
相關標籤/搜索