溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

C語言中動態(tài)內(nèi)存管理實(shí)例分析

發(fā)布時(shí)間:2022-06-18 09:43:45 來源:億速云 閱讀:153 作者:zzz 欄目:開發(fā)技術(shù)

今天小編給大家分享一下C語言中動態(tài)內(nèi)存管理實(shí)例分析的相關(guān)知識點(diǎn),內(nèi)容詳細(xì),邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

    1.動態(tài)內(nèi)存開辟的原因

    常見的內(nèi)存開辟方式

    int val = 20;//在??臻g上開辟四個字節(jié)的空間

    char arr[10] = {0};//在??臻g上開辟10個字節(jié)的連續(xù)空間

    上面開辟空間的方式有兩個特點(diǎn):

    1.空間開辟的大小是固定的;

    2. 數(shù)組在聲明的時(shí)候,必須指定數(shù)組的長度,它所需要的空間在編譯時(shí)分配;

    但是對于空間的需求,不只是上面的情況,有時(shí)候需要的空間大小在程序運(yùn)行的時(shí)候才能得知,這時(shí)候數(shù)組的編譯時(shí)開辟空間的方式就不能滿足了。

    所以需要動態(tài)開辟內(nèi)存

    2.動態(tài)內(nèi)存函數(shù)的介紹

    動態(tài)內(nèi)存的開辟都是在內(nèi)存的堆區(qū)中進(jìn)行開辟的

    2.1malloc和free

    C語言提供了一個動態(tài)開辟內(nèi)存的函數(shù):

    void* malloc(size_t size);

    malloc函數(shù)向內(nèi)存申請一塊連續(xù)可用的空間,并返回指向這塊空間起始位置的指針。

    1.如果開辟成功,則返回一個指向開辟好的空間的指針;

    2.如果開辟失敗,則返回一個NULL指針,因此malloc的返回值一定要做檢查,不然可能會造成野指針的問題;

    3.返回值的類型時(shí)void*,所以malloc函數(shù)并不知道開辟空間的類型,具體在使用的時(shí)候由使用者自己來決定返回值的類型;

    4.如果參數(shù)size為0,此時(shí)malloc函數(shù)的行為是標(biāo)準(zhǔn)未定義的,取決于程序運(yùn)行時(shí)使用的編譯器;

    malloc的使用:

    vint main()
    {
    	int* p = (int*)malloc(40);//向內(nèi)存申請一塊40字節(jié)的空間,并對放回的指針類型轉(zhuǎn)換
    	if (p == NULL)//返回NULL指針時(shí)讓打印錯誤信息并讓程序結(jié)束
    	{
    		perror("malloc");
    		return 1;
    	}
    	int i = 0;
    	for (i = 0; i < 10; i++)
    	{
    		*(p + i) = i;
    	}
    	return 0;
    }

    要記得包含頭文件<stdlib.h>

    這樣就對開辟的內(nèi)存空間進(jìn)行了一個使用了,但是還有點(diǎn)問題,因?yàn)橄騼?nèi)存申請的空間沒有進(jìn)行釋放。

    所以這時(shí)就引入了另一個函數(shù)free

    C語言提供了另一個函數(shù)free,專門用來做動態(tài)內(nèi)存的釋放和回收的

    void free(void* ptr);

    free函數(shù)用來釋放動態(tài)開辟的內(nèi)存

    1.如果參數(shù)ptr指向的空間是不是動態(tài)開辟的,那么free函數(shù)的行為是未定義的;

    2.如果參數(shù) ptr是NULL指針,則free函數(shù)什么事也不做;

    malloc和free函數(shù)都聲明在stdlib.h頭文件中

    free的使用:

    int main()
    {
    	int* p = (int*)malloc(40);//向內(nèi)存申請一塊40字節(jié)的空間,并對放回的指針類型轉(zhuǎn)換
    	if (p == NULL)//返回NULL指針時(shí)讓打印錯誤信息并讓程序結(jié)束
    	{
    		perror("malloc");
    		return 1;
    	}
    	int i = 0;
    	for (i = 0; i < 10; i++)
    	{
    		*(p + i) = i;
    	}
    	free(p);//釋放p所指向的動態(tài)內(nèi)存
    	p == NULL;//將p的值置為NULL
    	return 0;
    }

    free的參數(shù)一定是動態(tài)開辟內(nèi)存空間的那個起始位置的地址,否則會報(bào)錯

    在用free釋放完動態(tài)開辟的內(nèi)存之后,要對之前指向動態(tài)開辟空間的那個指針置為NULL,因?yàn)槟菈K動態(tài)開辟的空間已經(jīng)被操作系統(tǒng)回收了,沒有了訪問的權(quán)限,所以要讓p的值為NULL,避免野指針的問題。

    如果對動態(tài)內(nèi)存開辟的空間沒有釋放掉,會出現(xiàn)一個內(nèi)存泄漏的問題。

    2.2calloc

    C語言還提供了一個calloc函數(shù),calloc也是用來進(jìn)行動態(tài)內(nèi)存的分配

    void* calloc(size_t num, size_t size);

     1.calloc的功能是為num個字節(jié)大小為size的元素開辟一個空間,并且把空間的每個字節(jié)的數(shù)據(jù)都初始化為0,然后返回這塊連續(xù)空間的起始位置的地址;

    2.與malloc函數(shù)的區(qū)別只在于,calloc在返回地址之前會把申請的空間的每個字節(jié)的數(shù)據(jù)都初始化為全0;

    calloc的使用:

    int main()
    {
    	int* p = (int*)calloc(10, 4);
    	if (p == NULL)
    	{
    		perror("calloc");
    		return 1;
    	}
    	free(p);
    	p = NULL;
    	return 0;
    }

    內(nèi)存情況:

    C語言中動態(tài)內(nèi)存管理實(shí)例分析

     可以看到,動態(tài)開辟的40個字節(jié)的空間都被初始化為全0

    所以如果要對動態(tài)開辟的空間進(jìn)行初始化,可以直接使用calloc函數(shù)來完成

    2.3realloc

    有時(shí)會發(fā)現(xiàn)申請的空間太大或者太小了,為了合理的使用內(nèi)存,一定會對內(nèi)存的大小做一個靈活的調(diào)整,那么realloc函數(shù)就可以做到對動態(tài)開辟內(nèi)存大小的調(diào)整

    realloc函數(shù)的出現(xiàn)讓動態(tài)內(nèi)存管理更加靈活

    void* realloc (void* ptr, size_t size); 

    1.ptr是要調(diào)整的內(nèi)存空間;

    2.size是調(diào)整之后的新大小;

    3.返回值為調(diào)整之后的內(nèi)存起始位置;

    4.realloc函數(shù)在調(diào)整原內(nèi)存空間大小的基礎(chǔ)上,還會將原來內(nèi)存中的數(shù)據(jù)移動到新的空間;

    realloc函數(shù)在調(diào)整內(nèi)存空間時(shí)存在兩種情況:

    情況1:要調(diào)整的空間之后有足夠的空間來存放調(diào)整之后的大小

    C語言中動態(tài)內(nèi)存管理實(shí)例分析

    情況2:要調(diào)整的空間之后沒有足夠大的空間

    C語言中動態(tài)內(nèi)存管理實(shí)例分析

    如果是情況1,那么就在原有的內(nèi)存之后追加新的空間,原來空間的數(shù)據(jù)不發(fā)生變化。

    如果是情況2,原有空間之后沒有足夠多的空間,此時(shí)就會在堆空間上另找一個合適大小的連續(xù)空間來使用,這樣函數(shù)返回的是一個新的內(nèi)存地址 

    C語言中動態(tài)內(nèi)存管理實(shí)例分析

     并且realloc函數(shù)還會將原空間的數(shù)據(jù)移動到新的空間。

    如果realloc函數(shù)在堆區(qū)中都找不到一塊合適的空間,則會返回NULL指針。        

    realloc的使用:

    int main()
    {
    	int* p = (int*)calloc(10, 4);
    	if (p == NULL)
    	{
    		perror("calloc");
    		return 1;
    	}
    	p = realloc(p, 1000);
    	if (p == NULL)
    	{
    		perror("realloc");
    		return 1;
    	}
    	free(p);
    	p = NULL;
    	return 0;
    }

    其次,realloc函數(shù)還能使原有空間變?。?/p>

    使用:

    int main()
    {
    	int* p = (int*)calloc(10, 4);
    	if (p == NULL)
    	{
    		perror("calloc");
    		return 1;
    	}
    	p = realloc(p, 20);
    	if (p == NULL)
    	{
    		perror("realloc");
    		return 1;
    	}
    	free(p);
    	p = NULL;
    	return 0;
    }

    內(nèi)存情況:

    C語言中動態(tài)內(nèi)存管理實(shí)例分析

    3.常見的動態(tài)內(nèi)存錯誤

    3.1對NULL指針的解引用操作

    C語言中動態(tài)內(nèi)存管理實(shí)例分析

     這里編譯器直接把對NULL指針的解引用操作給取消掉了,如果在其他的編譯器下運(yùn)行,可能會出現(xiàn)問題,所以一定要對動態(tài)開辟內(nèi)存函數(shù)的返回值進(jìn)行一個NULL指針的判斷。

    3.2對動態(tài)開辟空間的越界訪問

    int main()
    {
    	int* p = (int*)malloc(40);
    	if (p == NULL)
    	{
    		perror("malloc");
    		return 1;
    	}
    	int i = 0;
    	for (i = 0; i <= 10; i++)
    	{
    		*(p + i) = i;
    	}
    	free(p);
    	p = NULL;
    	return 0;
    }

    其中*(p + 10) = 10;時(shí)對動態(tài)開辟的空間進(jìn)行了一個越界訪問了,編譯器直接報(bào)錯

    C語言中動態(tài)內(nèi)存管理實(shí)例分析

    3.3對非動態(tài)開辟內(nèi)存使用free

    對棧區(qū)上的空間使用free:

    int main()
    {
    	int a = 0;
    	free(&a);
    	return 0;
    }

    此時(shí)編譯器也會給出一個錯誤 

    C語言中動態(tài)內(nèi)存管理實(shí)例分析

    3.4使用釋放一塊動態(tài)開辟內(nèi)存的一部分

    int main()
    {
    	int* p = (int*)malloc(40);
    	p++;
    	free(p);
    	return 0;
    }

    此時(shí)p沒有指向動態(tài)開辟內(nèi)存的起始位置

    編譯器同樣給出了一個錯誤

    C語言中動態(tài)內(nèi)存管理實(shí)例分析

    3.5對同一塊動態(tài)內(nèi)存多次釋放

    int main()
    {
    	int* p = (int*)malloc(40);
    	free(p);
    	free(p);
    	return 0;
    }

     p已經(jīng)釋放過了

    C語言中動態(tài)內(nèi)存管理實(shí)例分析

    3.6動態(tài)開辟內(nèi)存忘記釋放(內(nèi)存泄漏)

    在向內(nèi)存申請了一塊空間之后沒有對其進(jìn)行釋放會造成內(nèi)存泄漏的問題

    會迅速吃滿你的內(nèi)存

    int main()
    {
    	while (1)
    	{
    		malloc(40);
    	}
    	return 0;
    }

     如圖:

    C語言中動態(tài)內(nèi)存管理實(shí)例分析

    如果程序在沒有結(jié)束之前申請的內(nèi)存都沒有進(jìn)行釋放的話,就會出現(xiàn)內(nèi)存泄漏的問題。所以在申請好一塊內(nèi)存之后要記得對其進(jìn)行釋放。

    總結(jié):

    忘記釋放不再使用的動態(tài)內(nèi)存開辟的空間就會造成內(nèi)存泄漏的問題,而且動態(tài)開辟的空間要正確釋放。

    4.練習(xí)

    4.1練習(xí)1

    void GetMemory(char* p)
    {
    	p = (char*)malloc(100);
    }
    void Test(void)
    {
    	char* str = NULL;
    	GetMemory(str);
    	strcpy(str, "hello world");
    	printf(str);
    }

    請問運(yùn)行Test 函數(shù)會有什么樣的結(jié)果?

    先創(chuàng)建了一個字符指針變量賦值為NULL,然后調(diào)用函數(shù)GerMemory,調(diào)用函數(shù)時(shí)形參只是一份對實(shí)參的臨時(shí)拷貝,函數(shù)調(diào)用時(shí),申請了一塊動態(tài)開辟內(nèi)存,但是并沒有返回p,p在函數(shù)調(diào)用結(jié)束后銷毀了,所以此時(shí)str指向的還是NULL,strcpy使用時(shí)對NULL指針進(jìn)行了解引用,造成了非法訪問,野指針的問題,也造成了動態(tài)內(nèi)存錯誤

    4.1練習(xí)2

    char* GetMemory(void)
    {
    	char p[] = "hello world";
    	return p;
    }
    void Test(void)
    {
    	char* str = NULL;
    	str = GetMemory();
    	printf(str);
    }

    請問運(yùn)行Test 函數(shù)會有什么樣的結(jié)果?

    調(diào)用GetMemory函數(shù)時(shí)在棧區(qū)開辟了一塊數(shù)組的空間,而在函數(shù)調(diào)用結(jié)束后數(shù)組開辟的空間已經(jīng)被回收了,str接收了p的值,而p所指向的空間已經(jīng)被回收了,所以p所指向的值也會發(fā)生變化,所以此時(shí)printf(str);打印的會是未知的結(jié)果

    4.3練習(xí)3

    void GetMemory(char** p, int num)
    {
    	*p = (char*)malloc(num);
    }
    void Test(void)
    {
    	char* str = NULL;
    	GetMemory(&str, 100);
    	strcpy(str, "hello");
    	printf(str);
    }

    請問運(yùn)行Test 函數(shù)會有什么樣的結(jié)果?

     Getmemory函數(shù)時(shí)傳址調(diào)用,將申請的動態(tài)開辟內(nèi)存空間的起始位置地址給了str,所以能夠正常訪問開辟的內(nèi)存。不過沒有進(jìn)行free會造成內(nèi)存泄漏的問題。

    4.4練習(xí)4

    void Test(void)
    {
    	char* str = (char*)malloc(100);
    	strcpy(str, "hello");
    	free(str);
    	if (str != NULL)
    	{
    		strcpy(str, "world");
    		printf(str);
    	}
    }

    請問運(yùn)行Test 函數(shù)會有什么樣的結(jié)果?

    此時(shí)已經(jīng)str所指向的動態(tài)內(nèi)存空間已經(jīng)釋放掉了,會造成非法訪問。

    5.C/C++程序的內(nèi)存開辟

    C語言中動態(tài)內(nèi)存管理實(shí)例分析

    C/C++程序內(nèi)存分配的幾個區(qū)域:

    1.棧區(qū):在執(zhí)行函數(shù)時(shí),函數(shù)內(nèi)部變量的存儲單元都可以在棧上創(chuàng)建,函數(shù)結(jié)束時(shí)這些存儲單元自動被釋放。棧內(nèi)存分配運(yùn)算內(nèi)置于處理器的指令集中,效率很高,但是分配的內(nèi)存容量有限。棧區(qū)主要存放運(yùn)行函數(shù)而存放的局部變量、函數(shù)參數(shù)、返回?cái)?shù)據(jù)、返回地址等。

    2.堆區(qū):一般有程序員分配,若程序員不釋放,程序結(jié)束時(shí)可能由OS回收,分配方式類似于鏈表。

    3.數(shù)據(jù)段(靜態(tài)區(qū)):存放全局變量,靜態(tài)數(shù)據(jù),程序結(jié)束后由系統(tǒng)釋放。

    4.代碼段:存放函數(shù)體的二進(jìn)制代碼

    實(shí)際上普通的局部變量是在棧區(qū)分配空間的,棧區(qū)的特點(diǎn)是在上面創(chuàng)建的變量出了作用域就銷毀,但是被static修飾的變量存放在數(shù)據(jù)段,數(shù)據(jù)段的特點(diǎn)是,在上面創(chuàng)建的變量,直到程序結(jié)束才銷毀,所以static修飾的變量生命周期變長了。

    以上就是“C語言中動態(tài)內(nèi)存管理實(shí)例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學(xué)習(xí)更多的知識,請關(guān)注億速云行業(yè)資訊頻道。

    向AI問一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI