欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C語言學(xué)習(xí)之柔性數(shù)組詳解

 更新時間:2023年03月27日 16:06:11   作者:努力學(xué)習(xí)游泳的魚  
結(jié)構(gòu)體的最后一個元素允許是未知大小的數(shù)組,這就叫柔性數(shù)組。這篇文中主要為大家詳細介紹了C語言中柔性數(shù)組的相關(guān)知識,需要的可以了解一下

一、前言

仔細觀察下面的代碼,有沒有看出哪里不對勁?

struct S
{
    int i;
    double d;
    char c;
    int arr[];
};

還有另外一種寫法:

struct S
{
    int i;
    double d;
    char c;
    int arr[0];
};

你應(yīng)該一眼就看到了,結(jié)構(gòu)體的最后一個成員數(shù)組的寫法是int arr[];或者是int arr[0],這兩種寫法是等價的,意思是這個數(shù)組的大小是不確定的、未知的、可以變化的。

C99允許這種特殊的結(jié)構(gòu)體存在。這樣的結(jié)構(gòu)體滿足下面兩個條件:

1.最后一個成員變量是一個大小可以變化的數(shù)組。

2.這個成員數(shù)組前面至少有另外一個成員變量。

我們稱這個大小可以變化的成員數(shù)組為柔性數(shù)組。

注意,柔性數(shù)組不能是結(jié)構(gòu)體里唯一一個成員,下面的代碼是不允許的:

struct S
{
    int arr[0];
};

這篇文章里,我將重點探討柔性數(shù)組的用法、內(nèi)存分布以及和優(yōu)勢。

二、柔性數(shù)組的用法

我不建議在棧上直接定義有柔性數(shù)組的結(jié)構(gòu)體,也就是這么寫:

struct S s;

因為柔性數(shù)組的大小是可以變化的,我建議在堆上申請空間,采取動態(tài)內(nèi)存管理的方法,這樣就能發(fā)揮出柔性數(shù)組大小可以改變的優(yōu)勢。

假設(shè)我們使用malloc()函數(shù)來開辟空間,一開始應(yīng)該malloc出多大的空間呢?要回答這個問題,首先我們要知道sizeof(struct S)是多少。

事實上,sizeof(struct S)計算出來的結(jié)果是該結(jié)構(gòu)體不考慮柔性數(shù)組的大小。如果我們想要給柔性數(shù)組開辟空間,malloc出來的大小應(yīng)該是sizeof(struct S)加上柔性數(shù)組的大小。

假設(shè)這個柔性數(shù)組在結(jié)構(gòu)體中的聲明是int arr[0];,我想給這個數(shù)組的大小是40個字節(jié),這樣這個數(shù)組就能存儲10個int,那么一開始malloc的大小就應(yīng)該是sizeof(struct S)+10*sizeof(int),具體的例子如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

int main()
{
	struct S* ps = (struct S*)malloc(sizeof(struct S) + 10 * sizeof(int));
	if (ps == NULL)
	{
		printf("malloc()->%s\n", strerror(errno));
		return 1;
	}

	return 0;
}

該結(jié)構(gòu)體中的i,d,c等變量可以正常使用。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

int main()
{
	struct S* ps = (struct S*)malloc(sizeof(struct S) + 10 * sizeof(int));
	if (ps == NULL)
	{
		printf("malloc()->%s\n", strerror(errno));
		return 1;
	}

	ps->i = 10;
	ps->d = 3.14;
	ps->c = 'F';

	return 0;
}

柔性數(shù)組也可以像正常的數(shù)組一樣訪問,比如把1~10放進去。注意此時這個數(shù)組的容量是10個int,不能越界訪問。使用的例子如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

int main()
{
	struct S* ps = (struct S*)malloc(sizeof(struct S) + 10 * sizeof(int));
	if (ps == NULL)
	{
		printf("malloc()->%s\n", strerror(errno));
		return 1;
	}

	ps->i = 10;
	ps->d = 3.14;
	ps->c = 'F';

	for (int i = 0; i < 10; i++)
	{
		ps->arr[i] = i + 1;
	}

	for (int i = 0; i < 10; i++)
	{
		printf("%d ", ps->arr[i]);
	}
	printf("\n");

	return 0;
}

我們還可以對柔性數(shù)組擴容,如果我們想讓這個柔性數(shù)組的容量是20個int,整個結(jié)構(gòu)體的新的大小就是sizeof(struct S)+20*sizeof(int),因為sizeof(struct S)是不考慮柔性數(shù)組的大小時計算的結(jié)構(gòu)體大小。只需要對ps進行realloc就行了。實現(xiàn)代碼如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

int main()
{
	struct S* ps = (struct S*)malloc(sizeof(struct S) + 10 * sizeof(int));
	if (ps == NULL)
	{
		printf("malloc()->%s\n", strerror(errno));
		return 1;
	}

	ps->i = 10;
	ps->d = 3.14;
	ps->c = 'F';

	for (int i = 0; i < 10; i++)
	{
		ps->arr[i] = i + 1;
	}

	for (int i = 0; i < 10; i++)
	{
		printf("%d ", ps->arr[i]);
	}
	printf("\n");

	struct S* tmp = (struct S*)realloc(ps, sizeof(struct S) + 20 * sizeof(int));
	if (tmp == NULL)
	{
		printf("realloc()->%s\n", strerror(errno));
		return 1;
	}
	else
	{
		ps = tmp;
	}

	return 0;
}

擴容后的柔性數(shù)組的空間更大了,我們可以把11~20都放進去。實現(xiàn)代碼如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

int main()
{
	struct S* ps = (struct S*)malloc(sizeof(struct S) + 10 * sizeof(int));
	if (ps == NULL)
	{
		printf("malloc()->%s\n", strerror(errno));
		return 1;
	}

	ps->i = 10;
	ps->d = 3.14;
	ps->c = 'F';

	for (int i = 0; i < 10; i++)
	{
		ps->arr[i] = i + 1;
	}

	for (int i = 0; i < 10; i++)
	{
		printf("%d ", ps->arr[i]);
	}
	printf("\n");

	struct S* tmp = (struct S*)realloc(ps, sizeof(struct S) + 20 * sizeof(int));
	if (tmp == NULL)
	{
		printf("realloc()->%s\n", strerror(errno));
		return 1;
	}
	else
	{
		ps = tmp;
	}

	for (int i = 10; i < 20; i++)
	{
		ps->arr[i] = i + 1;
	}
	
	for (int i = 0; i < 20; i++)
	{
		printf("%d ", ps->arr[i]);
	}

	return 0;
}

當(dāng)然最后別忘了free掉ps,否則會導(dǎo)致內(nèi)存泄漏。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

int main()
{
	struct S* ps = (struct S*)malloc(sizeof(struct S) + 10 * sizeof(int));
	if (ps == NULL)
	{
		printf("malloc()->%s\n", strerror(errno));
		return 1;
	}

	ps->i = 10;
	ps->d = 3.14;
	ps->c = 'F';

	for (int i = 0; i < 10; i++)
	{
		ps->arr[i] = i + 1;
	}

	for (int i = 0; i < 10; i++)
	{
		printf("%d ", ps->arr[i]);
	}
	printf("\n");

	struct S* tmp = (struct S*)realloc(ps, sizeof(struct S) + 20 * sizeof(int));
	if (tmp == NULL)
	{
		printf("realloc()->%s\n", strerror(errno));
		return 1;
	}
	else
	{
		ps = tmp;
	}

	for (int i = 10; i < 20; i++)
	{
		ps->arr[i] = i + 1;
	}
	
	for (int i = 0; i < 20; i++)
	{
		printf("%d ", ps->arr[i]);
	}

	free(ps);
	ps = NULL;

	return 0;
}

對于柔性數(shù)組的使用,在上面的例子中,可以總結(jié)出幾個要點:

1.malloc出來的大小是sizeof(struct S)加上柔性數(shù)組的大小,calloc同理。

2.擴容時realloc出來的新大小也是sizeof(struct S)加上柔性數(shù)組的新大小。

3.每次使用malloc和realloc等函數(shù)時,需要檢查返回值,否則可能導(dǎo)致對NULL指針的解引用(這點是動態(tài)內(nèi)存管理的常識了)。

4.一定要記得柔性數(shù)組的容量是多少,不要越界訪問了,空間不夠記得擴容。

5.記得free,防止內(nèi)存泄漏。

三、柔性數(shù)組的內(nèi)存分布

柔性數(shù)組是結(jié)構(gòu)體的一個成員數(shù)組,在前面的例子中,整個結(jié)構(gòu)體都是在堆上malloc出來的。此時,整個結(jié)構(gòu)體都存儲在堆上的一塊連續(xù)的空間里,包括前面幾個成員變量i,d,c和柔性數(shù)組arr。也就是這樣:

只不過數(shù)組arr的大小是可以改變的,所以叫“柔性數(shù)組”。

有些朋友可能會說了,我不需要柔性數(shù)組也能實現(xiàn)類似這樣的效果呀!我在結(jié)構(gòu)體里存一個指針,指向一塊malloc出來的空間,這塊空間也是堆上的,可以動態(tài)管理。也就是說,像下面這樣定義結(jié)構(gòu)體:

struct S
{
    int i;
    double d;
    char c;
    int* arr;
};

這樣似乎還簡單一點,先malloc出一個struct S出來,malloc的大小就是sizeof(struct S),像這樣:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

int main()
{
	struct S* ps = (struct S*)malloc(sizeof(struct S));
	if (ps == NULL)
	{
		printf("malloc()->%s\n", strerror(errno));
		return 1;
	}

	return 0;
}

然后再malloc出10個int的大小出來,用結(jié)構(gòu)體中的arr指針來管理這塊空間,像這樣:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

int main()
{
	struct S* ps = (struct S*)malloc(sizeof(struct S));
	if (ps == NULL)
	{
		printf("malloc()->%s\n", strerror(errno));
		return 1;
	}

	ps->arr = (int*)malloc(10 * sizeof(int));
	if (ps->arr == NULL)
	{
		printf("2: malloc()->%s\n", strerror(errno));
		return 1;
	}

	return 0;
}

此時arr就可以當(dāng)成一個數(shù)組來使用了,比如把1~10放進去。同樣還是要注意不要越界訪問。示例代碼如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

int main()
{
	struct S* ps = (struct S*)malloc(sizeof(struct S));
	if (ps == NULL)
	{
		printf("malloc()->%s\n", strerror(errno));
		return 1;
	}

	ps->arr = (int*)malloc(10 * sizeof(int));
	if (ps->arr == NULL)
	{
		printf("2: malloc()->%s\n", strerror(errno));
		return 1;
	}

	for (int i = 0; i < 10; i++)
	{
		ps->arr[i] = i + 1;
	}
	
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", ps->arr[i]);
	}
	printf("\n");

	return 0;
}

你如果覺得空間不夠,還可以擴容。比如,你可以把結(jié)構(gòu)體中的arr進行realloc,新的大小能存放20個int。示例代碼如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

int main()
{
	struct S* ps = (struct S*)malloc(sizeof(struct S));
	if (ps == NULL)
	{
		printf("malloc()->%s\n", strerror(errno));
		return 1;
	}

	ps->arr = (int*)malloc(10 * sizeof(int));
	if (ps->arr == NULL)
	{
		printf("2: malloc()->%s\n", strerror(errno));
		return 1;
	}

	for (int i = 0; i < 10; i++)
	{
		ps->arr[i] = i + 1;
	}
	
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", ps->arr[i]);
	}
	printf("\n");

	int* tmp = (int*)realloc(ps->arr, 20 * sizeof(int));
	if (tmp == NULL)
	{
		printf("realloc()->%s\n", strerror(errno));
		return 1;
	}
	else
	{
		ps->arr = tmp;
	}

	return 0;
}

此時,你就可以把11~20也放進去。實現(xiàn)代碼如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

int main()
{
	struct S* ps = (struct S*)malloc(sizeof(struct S));
	if (ps == NULL)
	{
		printf("malloc()->%s\n", strerror(errno));
		return 1;
	}

	ps->arr = (int*)malloc(10 * sizeof(int));
	if (ps->arr == NULL)
	{
		printf("2: malloc()->%s\n", strerror(errno));
		return 1;
	}

	for (int i = 0; i < 10; i++)
	{
		ps->arr[i] = i + 1;
	}

	for (int i = 0; i < 10; i++)
	{
		printf("%d ", ps->arr[i]);
	}
	printf("\n");

	int* tmp = (int*)realloc(ps->arr, 20 * sizeof(int));
	if (tmp == NULL)
	{
		printf("realloc()->%s\n", strerror(errno));
		return 1;
	}
	else
	{
		ps->arr = tmp;
	}

	for (int i = 10; i < 20; i++)
	{
		ps->arr[i] = i + 1;
	}

	for (int i = 0; i < 20; i++)
	{
		printf("%d ", ps->arr[i]);
	}

	return 0;
}

最后別忘了把arr和ps都free掉,而且順序不能錯了。如果你先free掉了ps,結(jié)構(gòu)體就沒了,里面的arr就成為了野指針,內(nèi)存就泄露了。實現(xiàn)代碼如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

int main()
{
	struct S* ps = (struct S*)malloc(sizeof(struct S));
	if (ps == NULL)
	{
		printf("malloc()->%s\n", strerror(errno));
		return 1;
	}

	ps->arr = (int*)malloc(10 * sizeof(int));
	if (ps->arr == NULL)
	{
		printf("2: malloc()->%s\n", strerror(errno));
		return 1;
	}

	for (int i = 0; i < 10; i++)
	{
		ps->arr[i] = i + 1;
	}

	for (int i = 0; i < 10; i++)
	{
		printf("%d ", ps->arr[i]);
	}
	printf("\n");

	int* tmp = (int*)realloc(ps->arr, 20 * sizeof(int));
	if (tmp == NULL)
	{
		printf("realloc()->%s\n", strerror(errno));
		return 1;
	}
	else
	{
		ps->arr = tmp;
	}

	for (int i = 10; i < 20; i++)
	{
		ps->arr[i] = i + 1;
	}

	for (int i = 0; i < 20; i++)
	{
		printf("%d ", ps->arr[i]);
	}

	free(ps->arr);
	ps->arr = NULL;
	free(ps);
	ps = NULL;

	return 0;
}

那這種實現(xiàn)的內(nèi)存分布是怎么樣的呢?這個結(jié)構(gòu)體是存儲在堆上的,用ps來管理,結(jié)構(gòu)體里的一個指針arr又指向了堆上的另一塊空間,如下圖:

這種實現(xiàn)方式和柔性數(shù)組的方式感覺差不多呀!都是在堆上有個結(jié)構(gòu)體,結(jié)構(gòu)體里有個大小可以變化的數(shù)組。那為什么非要搞出來個柔性數(shù)組的概念呢?那是因為,柔性數(shù)組有它獨特的優(yōu)勢。

四、柔性數(shù)組的優(yōu)勢

前面我們先用柔性數(shù)組實現(xiàn)了一種效果,又不使用柔性數(shù)組實現(xiàn)了相似的效果,對比兩種實現(xiàn)方式,我們可以做一些總結(jié):

1.使用上:柔性數(shù)組malloc了一次,free了一次;不使用柔性數(shù)組要malloc兩次,free兩次。柔性數(shù)組的使用更加簡單,不容易出錯。如果不使用柔性數(shù)組,可能會忘記free掉結(jié)構(gòu)體里的arr指針,導(dǎo)致內(nèi)存泄漏。

2.效率上:柔性數(shù)組的存儲空間是連續(xù)的,訪問時效率更高。

所以,雖然有相似的效果,我更推薦使用柔性數(shù)組的方式。

五、總結(jié)

在這篇博客里,重點需要掌握以下幾點:

1.如果結(jié)構(gòu)體里最后一個成員變量是一個數(shù)組,并且大小可以變化,這個成員數(shù)組就叫做柔性數(shù)組。一個結(jié)構(gòu)體里,除了柔性數(shù)組外必須至少有一個成員變量。

2.使用sizeof計算含有柔性數(shù)組的結(jié)構(gòu)體大小時,只計算除柔性數(shù)組之外的空間大小。

3.使用柔性數(shù)組,比不使用柔性數(shù)組操作更加簡單,不易出錯,且效率更高。

到此這篇關(guān)于C語言學(xué)習(xí)之柔性數(shù)組詳解的文章就介紹到這了,更多相關(guān)C語言 柔性數(shù)組內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C++常用字符串函數(shù)大全(2)

    C++常用字符串函數(shù)大全(2)

    這篇文章主要給大家分享的是C++常用字符串函數(shù)的大全,cstring.h庫即C語言中的string.h庫,它是C語言中為字符串提供的標(biāo)準(zhǔn)庫。C++對此進行了兼容,所以我們在C++當(dāng)中一樣可以使用,下面文章的詳細內(nèi)容,需要的朋友可以參考一下
    2021-11-11
  • C++實現(xiàn)重載矩陣的部分運算符

    C++實現(xiàn)重載矩陣的部分運算符

    這篇文章主要為大家詳細介紹了如何利用C++實現(xiàn)重載矩陣的部分運算符,文中的示例代碼講解詳細,對我們學(xué)習(xí)C++有一定幫助,需要的可以參考一下
    2022-10-10
  • 解讀C++編程的相關(guān)文件操作

    解讀C++編程的相關(guān)文件操作

    這篇文章主要介紹了解讀C++編程的相關(guān)文件操作,是C++入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下
    2015-09-09
  • C++實現(xiàn)統(tǒng)計代碼運行時間計時器的簡單實例

    C++實現(xiàn)統(tǒng)計代碼運行時間計時器的簡單實例

    這篇文章主要介紹了 C++實現(xiàn)統(tǒng)計代碼運行時間計時器的簡單實例的相關(guān)資料,需要的朋友可以參考下
    2017-07-07
  • C++實現(xiàn)LeetCode(67.二進制數(shù)相加)

    C++實現(xiàn)LeetCode(67.二進制數(shù)相加)

    這篇文章主要介紹了C++實現(xiàn)LeetCode(67.二進制數(shù)相加),本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下
    2021-07-07
  • C語言入門篇--初識結(jié)構(gòu)體

    C語言入門篇--初識結(jié)構(gòu)體

    本篇文章是基礎(chǔ)篇,適合c語言剛?cè)腴T的朋友,本文對c語言的結(jié)構(gòu)體做了簡單的分析,幫助大家快速入門c語言的世界,更好的理解c語言
    2021-08-08
  • C語言如何利用ASCII碼表統(tǒng)計字符串每個字符出現(xiàn)的次數(shù)

    C語言如何利用ASCII碼表統(tǒng)計字符串每個字符出現(xiàn)的次數(shù)

    這篇文章主要介紹了C語言如何利用ASCII碼表統(tǒng)計字符串每個字符出現(xiàn)的次數(shù),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-01-01
  • MFC自定義消息的實現(xiàn)方法

    MFC自定義消息的實現(xiàn)方法

    這篇文章主要介紹了MFC自定義消息的實現(xiàn)方法,通過該示例可以更好的理解MFC的消息封裝機制,以便更加靈活的打造個性化的windows應(yīng)用程序,需要的朋友可以參考下
    2014-07-07
  • C++模板非類型形參的詳細講解

    C++模板非類型形參的詳細講解

    這篇文章主要給大家介紹了關(guān)于C++模板非類型形參的相關(guān)資料,文中通過實例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作就有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2021-11-11
  • Linux下semop等待信號時出現(xiàn)Interrupted System Call錯誤(EINTR)解決方法

    Linux下semop等待信號時出現(xiàn)Interrupted System Call錯誤(EINTR)解決方法

    本篇文章是對在Linux下semop等待信號時出現(xiàn)Interrupted System Call錯誤(EINTR)的解決方法進行了詳細的分析介紹,需要的朋友參考下
    2013-05-05

最新評論