C語言?超詳細(xì)梳理總結(jié)動(dòng)態(tài)內(nèi)存管理
一.為什么存在動(dòng)態(tài)內(nèi)存分配
我們已經(jīng)掌握的內(nèi)存開辟方式有:
int a = 10;//在棧空間開辟4個(gè)字節(jié)的連續(xù)空間 int b[20] = { 0 };//在??臻g開辟20個(gè)字節(jié)的連續(xù)空間
這種開辟空間的方式有以下特點(diǎn):
1.開辟空間的大小是固定的
2.開辟數(shù)組時(shí)必須指定大小
初學(xué)數(shù)組時(shí),我寫過下面的錯(cuò)誤代碼。
int N; scanf("%d",&N); int a[N]={ 0 };
可N是變量,不能用于數(shù)組元素個(gè)數(shù)的初始化。
如果我們需要的空間大小在程序運(yùn)行時(shí)才能知道,那就只能試試動(dòng)態(tài)內(nèi)存開辟了。
二.動(dòng)態(tài)內(nèi)存函數(shù)的介紹
1.malloc和free
void* malloc (size_t size);
void free (void* ptr);
malloc函數(shù)用于向內(nèi)存申請(qǐng)一塊連續(xù)可用的空間,并且返回指向這塊空間的指針。
若開辟成功,返回指向這塊空間的指針
若開辟失敗,返回NULL指針,因此malloc的返回值一定要做檢查
使用完malloc函數(shù)要用free釋放申請(qǐng)的內(nèi)存空間
#include<stdio.h> #include<stdlib.h> int main() { int* p = (int*)malloc(40);//開辟40個(gè)字節(jié)的棧空間 if (p == NULL) //檢查是否為空指針 { perror("malloc"); return 1; } for (int i = 0; i < 10; i++) { *(p + i)=i; } free(p); //用完后釋放空間,注意參數(shù)為首地址 p = NULL; //置為空指針 }
2.calloc
void* calloc (size_t num, size_t size)
calloc的兩個(gè)參數(shù)分別為申請(qǐng)?jiān)氐膫€(gè)數(shù)和每個(gè)元素的大小,
使用和malloc差不多,但是申請(qǐng)的空間會(huì)被初始化為0,
#include<stdio.h> #include<stdlib.h> int main() { int* p = (int*)calloc(10, sizeof(int)); if (p == NULL) { perror("calloc"); return 1; } for (int i = 0; i < 10; i++) { printf("%d ", *(p + i)); //輸出為 10個(gè)0 } free(p); p = NULL; }
3.realloc
void* realloc (void* ptr, size_t size)
realloc用于重新設(shè)置要開辟內(nèi)存空間的大小,可以是增大或減小
指針ptr是指向先前使用 malloc、calloc 或 realloc 分配的內(nèi)存塊的指針。
size 是新開辟內(nèi)存空間的大小
若原空間后面未開辟的空間足夠使用,則返回原先的起始地址
若原空間后面未開辟的空間不足以填滿新開辟內(nèi)存空間,
則會(huì)在某個(gè)地址開辟所需要的空間,free掉原空間的地址,
并且返回新的地址的起始地址
真 · 一條龍服務(wù)
若擴(kuò)容失敗,會(huì)返回空指針,因此也要檢查是否是空指針
三.常見的動(dòng)態(tài)內(nèi)存錯(cuò)誤
1.對(duì)NULL指針的解引用操作
void test() { int *p = (int*)malloc(INT_MAX/4); *p = 20; free(p); }
若p為空指針,則程序錯(cuò)誤。
解決方案:檢查是否為空指針
2.對(duì)動(dòng)態(tài)開辟空間的越界訪問
int main() { int* p = (int*)calloc(10, sizeof(int)); if (p == NULL) { perror("calloc"); return 1; } for (int i = 0; i <= 10; i++) //當(dāng)i為10時(shí),形成越界訪問,程序出錯(cuò) { printf("%d ", *(p + i)); } free(p); p = NULL; }
使用這塊空間時(shí)要注意是否已經(jīng)越界訪問
3.對(duì)非動(dòng)態(tài)開辟的空間使用free釋放
int a = 10; int* p = &a; free(p);
一執(zhí)行,程序崩潰了
4.使用free釋放一塊動(dòng)態(tài)開辟空間的一部分
void test() { int* p = (int*)malloc(100); p++; free(p); }
同樣會(huì)崩潰
5.對(duì)同一塊開辟的空間多次釋放
void test() { int* p = (int*)malloc(100); free(p); free(p); }
沒錯(cuò),又又又崩潰了,就不上圖了
6.動(dòng)態(tài)內(nèi)存開辟忘記釋放(內(nèi)存泄漏)
如果使用空間后不釋放,會(huì)導(dǎo)致內(nèi)存泄漏。
內(nèi)存泄漏的堆積,這會(huì)最終消耗盡系統(tǒng)所有的內(nèi)存。
四.幾個(gè)經(jīng)典的筆試題
第一個(gè)
void GetMemory(char* p) //對(duì)空指針解引用 { p = (char*)malloc(100); //內(nèi)存泄露 } void test() { char* str = NULL; GetMemory(str); strcpy(str, "hello world"); printf(str); } int main() { test(); }
p是str的一份臨時(shí)拷貝,指向malloc申請(qǐng)的起始地址,
出了函數(shù)之后,內(nèi)存還給系統(tǒng),str仍為空指針,strcpy把“hello world”放進(jìn)空指針
第二個(gè)
char *GetMemory(void) { char p[] = "hello world"; return p; } void test() { char* str = NULL; str=GetMemory(); //野指針str printf(str); } int main() { test(); }
定義字符串p,并返回p的地址
但是當(dāng)出了這個(gè)函數(shù),內(nèi)存還給系統(tǒng),沒有使用權(quán)限
指針變?yōu)?/p>
第三個(gè)
void GetMemory(char** p,int num) //傳址調(diào)用 { *p = (char*)malloc(num); } void test() { char* str = NULL; GetMemory(&str,100); strcpy(str, "hello world"); printf(str); //沒有free } int main() { test(); }
打印hello world
沒有釋放空間
第四個(gè)
void GetMemory(char** p,int num) { *p = (char*)malloc(num); } void test() { char* str = (char*)malloc(100); strcpy(str, "hello world"); free(str); //還給你,我還要用,哼~ if (str != NULL) { strcpy(str, "!!!"); printf(str); } } int main() { test(); }
開辟100個(gè)字節(jié)的空間后,又把這塊空間還給操作系統(tǒng)。
再次把“!??!”放進(jìn)這塊空間,非法修改
tips:動(dòng)態(tài)內(nèi)存管理是在堆區(qū)上進(jìn)行的。
到此這篇關(guān)于C語言 超詳細(xì)梳理總結(jié)動(dòng)態(tài)內(nèi)存管理的文章就介紹到這了,更多相關(guān)C語言 動(dòng)態(tài)內(nèi)存管理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Linux環(huán)境g++編譯GDAL動(dòng)態(tài)庫操作方法
下面小編就為大家?guī)硪黄狶inux環(huán)境g++編譯GDAL動(dòng)態(tài)庫操作方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-05-05詳解C++的靜態(tài)內(nèi)存分配與動(dòng)態(tài)內(nèi)存分配
內(nèi)存分配 (Memory Allocation) 是指為計(jì)算機(jī)程序或服務(wù)分配物理內(nèi)存空間或虛擬內(nèi)存空間的一個(gè)過程,本文主要介紹了C++的靜態(tài)內(nèi)存分配與動(dòng)態(tài)內(nèi)存分配,感興趣的同學(xué)可以參考閱讀2023-06-06C語言進(jìn)程程序替換的實(shí)現(xiàn)詳解
為什么要進(jìn)程替換?因?yàn)楦高M(jìn)程創(chuàng)建出來的子進(jìn)程和父進(jìn)程擁有相同的代碼段,所以,子進(jìn)程看到的代碼和父進(jìn)程是一樣的。當(dāng)我們想要讓子進(jìn)程執(zhí)行不同的程序時(shí)候,就需要讓子進(jìn)程調(diào)用進(jìn)程程序替換的接口,從而讓子進(jìn)程執(zhí)行不一樣的代碼2022-08-08C++中string類的常用方法實(shí)例總結(jié)
string類是C++提供的抽象數(shù)據(jù)類型,其支持可變長字符串,下面這篇文章主要給大家總結(jié)介紹了關(guān)于C++中string類的常用方法,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-03-03c# 實(shí)現(xiàn)獲取漢字十六進(jìn)制Unicode編碼字符串的實(shí)例
下面小編就為大家?guī)硪黄猚# 實(shí)現(xiàn)獲取漢字十六進(jìn)制Unicode編碼字符串的實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-01-01詳解C++循環(huán)創(chuàng)建多級(jí)目錄及判斷目錄是否存在的方法
這篇文章主要介紹了C++循環(huán)創(chuàng)建多級(jí)目錄及判斷目錄是否存在的方法,文中代碼有一個(gè)針對(duì)各種系統(tǒng)進(jìn)行判斷來加載不同頭文件的方法,需要的朋友可以參考下2016-03-03一起來學(xué)習(xí)C++的函數(shù)指針和函數(shù)對(duì)象
這篇文章主要為大家詳細(xì)介紹了C++函數(shù)指針和函數(shù)對(duì)象,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助2022-03-03