c語言-動態內存管理

文章目錄

    • 一、為什么會有動態內存管理
    • 二、申請內存函數
      • 1、malloc
      • 2、free
      • 3、calloc
      • 4、realloc
    • 三、常見的動態內存的錯誤
    • 四、練習


一、為什么會有動態內存管理

1.我們一般的開辟空間方式:

int a = 0;//申請4個字節空間
int arr[10] = { 0 };//申請40個字節空間

2.這樣開辟空間的特點

(1)申請的空間大小是固定的
(2)像數組那樣一開始就要確定大小,一旦確定大小就不能改變了

3.動態內存

對于程序來說上述的內存申請是不能滿足 因此為了能夠對內存進行調整,C語言引入了動態內存開辟,讓程序員自己可以申請和釋放空間,就比較靈活了。

二、申請內存函數

以下動態申請的內存都是向堆區申請的,
并且都申請內存的函數和釋放函數都包含在頭文件 :#include<stdlib.h>

1、malloc

(1)返回類型和參數:

void* malloc(size_t size);//返回類型為 void*  ,參數為正整數單位是字節

因為返回類型為 void*,所以在malloc函數是不知道我們想要申請什么類型的空間,面對這種情況我們要將返回的類型進行強制類型轉換,這樣就能返回我們需要的類型了,參數是申請的大小

(2)作用:

向內存申請一塊連續的空間,并返回指向這塊空間的的指針

(3)注意:

a.在申請完之后我們還要判斷是否成功申請
當申請失敗時會返回NULL
當申請成功后就返回指向這塊空間的的指針,這樣可以正常使用這塊空間了
b.如果參數 size 為0,malloc的行為是標準是未定義的,取決于編譯器

(4)使用:

int main() {int* p = (int*)malloc(sizeof(int) * 10);//向內存申請40個字節空間if (p == NULL)//判斷是否申請成功return 1;//失敗直接放回for (int i = 0; i < 10; i++)//成功就正常使用*(p + i) = i;for (int i = 0; i < 10; i++)//打印printf("%d ", *(p + i));return 0;
}

運行結果:
在這里插入圖片描述

2、free

(1)返回類型和參數:

void free( void* p)//參數為向動態內存申請的空間的指針,返回類型為空

(2)作用:

專門進行對動態內存的釋放和回收

(3)注意:

a.當釋放的內存不是動態開辟的,這是free未定義的
b.當p是NULL時,free函數什么事都不發生
c.當我們將p指向的空間釋放后,要將p置空,不然p就成野指針了

(4)使用

在我們上一個代碼中,并未對malloc函數開辟的空間進行釋放,其實這是不對的,這會造成內存泄漏。 那么我們就來用一下free函數吧

int main() {int* p = (int*)malloc(sizeof(int) * 10);//向內存申請40個字節空間if (p == NULL)//判斷是否申請成功return 1;//失敗直接放回for (int i = 0; i < 10; i++)//成功就正常使用*(p + i) = i;for (int i = 0; i < 10; i++)//打印printf("%d ", *(p + i));free(p);//釋放p = NULL;//及時置空,防止出現野指針return 0;
}

這樣代碼才算完整。

3、calloc

(1)返回類型和參數:

void *calloc(size_t n,size_t  size);

返回類型為 void* ,所以和malloc一樣想要什么類型的空間就進行強制轉換類型即可,第一個參數為申請的個數,第二個參數為申請的類型的空間大小(單位為字節)

(2)作用:

申請一塊連續的空間,并將空間的內容全部初始化為0,然后返回指向這塊空間的指針

(3)注意:

a.在申請完之后我們還要判斷是否成功申請
當申請失敗時會返回NULL
當申請成功后就返回指向這塊空間的的指針,這樣可以正常使用這塊空間了
b.如果參數 size 為0,malloc的行為是標準是未定義的,取決于編譯器
c.與malloc的區別就是calloc會將申請的空間初始化,這樣使用時更方便

(4)使用:

int main() {int* p = (int*)calloc(10,sizeof(int));//申請if (p == NULL) {//判斷printf("NULL");return 1;}//使用for (int i = 0; i < 10; i++)*(p + i) = i;for (int i = 0; i < 10; i++)printf("%d ", *(p + i));free(p);//同樣的釋放空間p = NULL;//置空return 0;
}

運行結果:
在這里插入圖片描述

4、realloc

(1)返回類型和參數:

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

返回類型為 void* 所以和calloc一樣想要什么類型就強制類型轉換,第一個參數p為指向想要的改變的空間的 指針,第二參數為改變的大小(單位為字節)

(2)作用:

在原來的動態內存的空間上增大或者縮小,并返回改變后指向新的空間的指針

(3)注意:

a.開辟失敗返回空指針
b.開辟的新的空間時有兩種開辟的方式,第一種是我們原本的空間后面有足夠的空間,那樣直接在原來的空間后面擴容,第二種是我們原本的空間后面沒有足夠的空間,那樣的話,系統就會在內存上找一塊適合的空間重新開辟,并將原來空間的內容復雜過去,再將原來的空間銷毀。
由于開辟的情況有兩種,所以我們使用時要注意開辟空間失敗這種情況
如:我們使用了第二種情況開辟空間、并直接用原來的指針去接收指向開辟的空間的指針,如果開辟失敗的話返回NULL,這導致原來的數據會丟失。
所以為了解決這個隱患,我們可以重新定義一個指針來接收,判斷不為空再將該指針賦給原來的指針。

圖:
在這里插入圖片描述
(4)使用:

int main() {//先用動態內存開辟一個空間int* p = (int*)calloc(10, sizeof(int));if (p == NULL)return 1;for (int i = 0; i < 10; i++)*(p + i) = i;for (int i = 0; i < 10; i++)printf("%d ", *(p + i));printf("\n");//使用realloc增加空間int* pp = (int*)realloc(p, sizeof(int) * 15);//再申請一個指針變量來接收if (pp == NULL)return 1;elsep = pp;//不為空再賦給原來的指針for (int i = 10; i < 15; i++)*(p + i) = i;for (int i = 10; i < 15; i++)printf("%d ", *(p + i));free(p);//最后不要忘記了釋放并置空p = NULL;return 0;
}

運行結果:
在這里插入圖片描述

三、常見的動態內存的錯誤

1、對NULL指針的解引用操作

void test(){int *p = (int *)malloc(INT_MAX/4);//開辟空間//這里我們不知道是否開辟成功*p = 20;//如果p的值是NULL,就會有問題free(p);p=NULL;}

這里的問題是不知道p為不為NULL
改:

void test(){int *p = (int *)malloc(INT_MAX/4);//開辟空間if(p!=NULL)*p = 20;free(p);p=NULL;}

2 、對動態開辟空間的越界訪問

void test(){int i = 0;int *p = (int *)malloc(10*sizeof(int));if(NULL == p){exit(EXIT_FAILURE);}for(i=0; i<=10; i++){*(p+i) = i;//當i是10的時候越界訪問}free(p);p=NULL;}

改:

void test(){int i = 0;int *p = (int *)malloc(10*sizeof(int));if(NULL == p){exit(EXIT_FAILURE);}for(i=0; i<10; i++)//當我們改成<10之后i就不會到10,也就不會越界了{*(p+i) = i;}free(p);p=NULL;}

3 、對非動態開辟內存使用free釋放

 void test(){int a = 10;int *p = &a;free(p);//ok?}

這種行為在free函數中未定義,最好不要使用
4 、使用free釋放?塊動態開辟內存的?部分

void test(){int *p = (int *)malloc(100);p++;free(p);//p不再指向動態內存的起始位置}

這種行為會導致內存泄漏
5 、對同?塊動態內存多次釋放

void test(){int *p = (int *)malloc(100);free(p);free(p);//重復釋放}

當出現這種情況程序會崩掉
6、 動態開辟內存忘記釋放(內存泄漏)

void test(){int *p = (int *)malloc(100);if(NULL != p){*p = 20;}}
int main(){test();while(1);}

這種未對申請的空間釋放,會導致內存泄漏

四、練習

1、請問運行Test 函數會有什么樣的結果?

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

運行結果:
在這里插入圖片描述
什么都沒有輸出,這是為什么呢?
這是因為調用GetMemory函數時使用的是傳值調用,p雖然申請到了空間,但是并沒有改變str的值,所以str=NULL,所以什么內容都沒有打印,
改;我們可以將傳值調用改為傳址調用
如:

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

運行結果:
在這里插入圖片描述
成功打印
2、請問運行Test 函數會有什么樣的結果?

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

運行結果:
在這里插入圖片描述
出現了隨機值,這是為什么呢?
因為p是在棧區創建,當函數結束后該p指向的空間也會銷毀然后返還給內存,此時將p傳給str之后,str就會變成野指針,它指向的空間打印出來的就是隨機值了
改:我們可以用動態內存
如:

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

運行結果:
在這里插入圖片描述
3、請問運行Test 函數會有什么樣的結果?

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

運行結果:
在這里插入圖片描述
輸出正確。但是這里的問題時沒有釋放動態內存
改:

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

4、請問運行Test 函數會有什么樣的結果?

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

運行結果:
在這里插入圖片描述
雖然結果正確,但是其實是有問題的
1.因為str存儲的地址不會改變,應該手動置空,但是它釋放空間后沒有置空
2.使用釋放的空間(這塊空間已經還給系統了)
3.為什么還能打印world呢?那是因為該塊空間沒有被覆蓋,world還在那里
我們可以試試再他前面再申請一次動態內存看看

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

運行結果:
在這里插入圖片描述
world被覆蓋了,就打印不出了
改:我們可以釋放后置空,就不會出現這種情況了

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

以上就是我的分享了,如果有什么錯誤,歡迎在評論區留言。
最后,謝謝大家的觀看!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/207807.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/207807.shtml
英文地址,請注明出處:http://en.pswp.cn/news/207807.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

解決在Linux中進行redis的主從復制時出現的從機可以獲取到主機的信息,主機獲取不到從機的信息~

主機&#xff1a; 從機1&#xff1a; 從機2&#xff1a; 出現上述的原因是我在redis.conf中設置了密碼&#xff0c;那么就導致了我在進行主從復制時&#xff0c;需要進行密碼驗證&#xff0c;然后我在網上查閱了很多資料&#xff0c;有的說讓在從機中指定密碼&#xff0c;有的說…

一對多聊天室

多人聊天包 由于要先創建服務面板&#xff0c;接收客戶端連接的信息&#xff0c;此代碼使用順序為先啟動服務端&#xff0c;在啟動客戶端&#xff0c;服務端不用關&#xff0c;不然會報錯。多運行幾次客戶端&#xff0c;實現單人聊天 創建服務面板 package yiduiduo;import j…

【頭歌系統數據庫實驗】實驗7 SQL的復雜多表查詢-1

目錄 第1關&#xff1a;求各顏色零件的平均重量 第2關&#xff1a;求北京和天津供應商的總個數 第3關&#xff1a;求各供應商供應的零件總數 第4關&#xff1a;求各供應商供應給各工程的零件總數 第5關&#xff1a;求重量大于所有零件平均重量的零件名稱 第6關&#xff1…

初識人工智能,一文讀懂人工智能概論(1)

&#x1f3c6;作者簡介&#xff0c;普修羅雙戰士&#xff0c;一直追求不斷學習和成長&#xff0c;在技術的道路上持續探索和實踐。 &#x1f3c6;多年互聯網行業從業經驗&#xff0c;歷任核心研發工程師&#xff0c;項目技術負責人。 &#x1f389;歡迎 &#x1f44d;點贊?評論…

Python Django-allauth: 構建全面的用戶身份驗證系統

更多資料獲取 &#x1f4da; 個人網站&#xff1a;ipengtao.com Django-allauth是一個功能強大的Django插件&#xff0c;旨在簡化和定制Web應用程序中的用戶身份驗證和管理。本文將深入介紹Django-allauth的核心功能、基本用法以及實際應用場景&#xff0c;通過豐富的示例代碼…

AWTK 串口屏開發(1) - Hello World

1. 功能 這個例子很簡單&#xff0c;制作一個調節溫度的界面。在這里例子中&#xff0c;模型&#xff08;也就是數據&#xff09;里只有一個溫度變量&#xff1a; 變量名數據類型功能說明溫度整數溫度。范圍 (0-100) 攝氏度 2. 創建項目 從模板創建項目&#xff0c;將 hmi/…

挑選在線客服系統的七大注意事項

越來越多的企業開始注重客戶服務&#xff0c;所以在線客服系統也逐漸成為了電商企業不可或缺的一部分。然而在挑選在線客服系統的過程中&#xff0c;蠻多企業會遇到各種各樣的問題&#xff0c;這就導致了最終選擇的系統并不適合自己企業的需求。接下來我將提醒大家挑選在線客服…

網絡運維與網絡安全 學習筆記2023.12.4

網絡運維與網絡安全 學習筆記 第三十四天 今日目標 訪問存儲設備、配置yum源、使用yum管理軟件 LAMP部署及測試、systemctl系統控制、SELinux-Firewall防護 訪問存儲設備 掛載/卸載設備 什么是掛載? 掛載&#xff0c;裝載 將光盤/U盤/分區/網絡存儲等設備裝到某個Linux目…

mysql中IGNORE 關鍵字段用法

在MySQL中&#xff0c;IGNORE 關鍵字通常與數據修改語句&#xff08;如INSERT、UPDATE和DELETE&#xff09;一起使用&#xff0c;其作用是使得操作在遇到錯誤時不會終止執行&#xff0c;而是忽略錯誤并繼續處理后續的數據。這對于處理可能包含重復鍵值或某些違反約束的批量操作…

ssm(springboot“昭愿”甜品店銷售管理系統 蛋糕商城系統Java

ssm(springboot“昭愿”甜品店銷售管理系統 蛋糕商城系統Java(code&LW) 開發語言&#xff1a;Java 框架&#xff1a;ssm/springboot vue JDK版本&#xff1a;JDK1.8&#xff08;或11&#xff09; 服務器&#xff1a;tomcat 數據庫&#xff1a;mysql 5.7&#xff08;或…

FastAPI如何返回文件字節流?并且附帶一些json參數

文章目錄 GET方法 StreamingResponsePOST方法 StreamingResponse其他關于壓縮 GET方法 StreamingResponse 服務器&#xff1a; from fastapi import FastAPI from fastapi.responses import StreamingResponse from starlette.responses import FileResponse from pydantic i…

pycharm debug的時候變量顯示不出來,一直Collecting data...問題解決

問題描述&#xff1a; 如圖所示&#xff1a;一直加載不出來變量&#xff0c;顯示Collecting data 解決辦法&#xff1a; 在setting中給下圖中的選項打勾 這下就可以了。 應該是調試時候有線程沖突&#xff0c;具體我也不太懂。

springboot(ssm勤工助學管理系統 勤工儉學網站Java(codeLW)

springboot(ssmBBS勤工助學管理系統 勤工儉學網站Java(code&LW) 開發語言&#xff1a;Java 框架&#xff1a;ssm/springboot vue JDK版本&#xff1a;JDK1.8&#xff08;或11&#xff09; 服務器&#xff1a;tomcat 數據庫&#xff1a;mysql 5.7&#xff08;或8.0&…

mysql服務日志打印,時區不對的問題

查資料發現 原來日志的時區和服務器的時區不是一個參數控制的 log_timestamps 單獨控制日志的時區 show global variables like log_timestamps;看到默認的是UTC&#xff0c;只需要修改為和系統一致就行 #數據庫中直接修改 set global log_timestampsSYSTEM;#配置文件my.cn…

springboot086靚車汽車銷售網站

springboot086靚車汽車銷售網站 成品項目已經更新&#xff01;同學們可以打開鏈接查看&#xff01;需要定做的及時聯系我&#xff01;專業團隊定做&#xff01;全程包售后&#xff01; 2000套項目視頻鏈接&#xff1a;https://pan.baidu.com/s/1N4L3zMQ9nNm8nvEVfIR2pg?pwd…

APP測試基本流程及測試點總結

APP測試基本流程及測試點總結 1 測試流程 1.1 流程圖 1.2 測試周期 測試周期可按項目的開發周期來確定測試時間&#xff0c;一般測試時間為兩三周&#xff08;即15個工作日&#xff09;&#xff0c;根據項目情況以及版本質量可適當縮短或延長測試時間。 1.3 測試資源 測試任務…

12.8每日一題(備戰藍橋杯分支練習)

12.8每日一題&#xff08;備戰藍橋杯分支練習&#xff09; 題目 1633: 【入門】判斷3的倍數題目描述輸入輸出樣例輸入樣例輸出來源/分類 題解 1633: 【入門】判斷3的倍數題目 1636: 【入門】超市賣電池題目描述輸入輸出樣例輸入樣例輸出來源/分類 題解 1636: 【入門】超市賣電池…

B : DS靜態查找之折半查找

Description 給出一個隊列和要查找的數值&#xff0c;找出數值在隊列中的位置&#xff0c;隊列位置從1開始 要求使用折半查找算法 Input 第一行輸入n&#xff0c;表示隊列有n個數據 第二行輸入n個數據&#xff0c;都是正整數&#xff0c;從小到大&#xff0c;用空格隔開 …

VQVAE

68、VQVAE預訓練模型的論文原理及PyTorch代碼逐行講解_嗶哩嗶哩_bilibili本期視頻主要講解大規模無監督預訓練模型之VQVAE的論文原理以及PyTorch代碼逐行講解&#xff0c;希望對大家理解VQVAE以及圖像生成有幫助。, 視頻播放量 9920、彈幕量 80、點贊數 485、投硬幣枚數 322、收…