企業(yè)網(wǎng)絡營銷策劃方案范文免費seo教程資源
他治愈了身邊所有人,唯獨沒有治愈他自己—超脫
csdn上的朋友你們好呀!!今天給大家分享的是動態(tài)內(nèi)存管理
👀為什么存在動態(tài)內(nèi)存分配
我們定義的局部變量在棧區(qū)創(chuàng)建
int n = 4;//在棧上開辟4個字節(jié)大小int arr[10] = { 0 };//在棧上開辟連續(xù)的40個字節(jié)大小
上述變量創(chuàng)建的特點
1. 空間開辟大小是固定的。
2. 數(shù)組在申明的時候,必須指定數(shù)組的長度,它所需要的內(nèi)存在編譯時分配。
但是對于空間的需求,不僅僅是上述的情況。有時候我們需要的空間大小在程序運行的時候才能知道,那數(shù)組的編譯時開辟空間的方式就不能滿足了
int main()
{int n;scanf("%d",&n);int arr[n];}
上述代碼只能在C99標準編譯器上才行,vs系列編譯器均不支持,那我們怎么才能在運行的時候,實現(xiàn)上述變長數(shù)組的代碼呢??這時候就只能試試動態(tài)存開辟了。
👀 動態(tài)內(nèi)存函數(shù)的介紹
malloc
函數(shù)功能:開辟內(nèi)存塊
參數(shù)size_t:需要申請的字節(jié)數(shù)
返回值:申請失敗返回空指針,申請成功返回指向申請該空間首地址的指針
頭文件:stdlib.h
注意:返回指針的類型是void*,這時候需要你把該指針強制類型轉(zhuǎn)化為你想要的類型,這樣方便訪問,以及解引用,malloc申請來的空間是連續(xù)的,但是多次malloc來的是不連續(xù)的
malloc的使用
int main()
{int*p=(int*) malloc(40);//申請了40個字節(jié),強制轉(zhuǎn)化為int*類型指針if (p == NULL)//如果返回空指針的話,申請失敗{perror("malloc:");//打印錯誤信息return 1;//非正常退出}for (int i = 0; i < 10; i++){*(p + i) = i;//對每一個四個字節(jié)大小的元素賦值,這里*(p+i)的本質(zhì)就是p[i];printf("%d", *(p + i));//打印每個元素}return 0;//程序正常退出}
free
功能:釋放內(nèi)存塊
參數(shù):指針接收要釋放內(nèi)存塊的首地址
頭文件:stdlib.h
返回值:無
了解了這些之后,我們試一下釋放剛才malloc來的內(nèi)存塊
int main()
{int i = 0;int*p=(int*) malloc(40);if (p == NULL){perror("malloc:");return 1;}for (int i = 0; i < 10; i++){*(p + i) = i;printf("%d", *(p + i));}free(p);//指針接收要釋放內(nèi)存塊的首地址p = NULL;//很有必要否則p為野指針return 0;}
當p所指向的申請的空間釋放時,p指針指向隨機位置,p變成野指針。
如果我們不釋放動態(tài)內(nèi)存申請的內(nèi)存的時候,程序結(jié)束,動態(tài)申請內(nèi)存由操作系統(tǒng)自動回收,如果不用free函數(shù)釋放申請好的空間,就會在程序運行結(jié)束前一直存在于堆中,造成內(nèi)存泄漏
int main()
{while (1){malloc(1000);}return 0;}
我是不知天高地厚的年輕人哈哈哈哈哈
calloc
功能:申請一個數(shù)組在內(nèi)存中,并且初始化為0;
參數(shù):size_t num申請數(shù)組元素的個數(shù),size_t size每個元素的字節(jié)大小
返回值:申請失敗返回空指針,申請成功返回指向申請該空間首地址的指針
頭文件:stdlib.h
calloc函數(shù)使用
int main()
{int i = 0;int*p=(int*) calloc(10,sizeof(int));//申請10個元素,每個元素字節(jié)大小4if (p == NULL)//如果返回空指針的話,申請失敗{perror("calloc:");//打印錯誤信息return 1;//非正常退出}for (int i = 0; i < 10; i++){printf("%d ", *(p + i));//打印初始化的值}free(p);p = NULL;return 0;}
malloc和calloc的區(qū)別:與函數(shù) malloc 的區(qū)別只在于 calloc 會在返回地址之前把申請的空間的每個字節(jié)初始化為全0
我們可以看看malloc有沒有先初始化
int main()
{int* p = (int*)malloc(40);//申請了40個字節(jié),強制轉(zhuǎn)化為int*類型指針if (p == NULL)//如果返回空指針的話,申請失敗{perror("malloc:");//打印錯誤信息return 1;//非正常退出}for (int i = 0; i < 10; i++){printf("%d ", *(p + i));//打印每個元素}free(p);//指針接收要釋放內(nèi)存塊的首地址p = NULL;//很有必要否則p為野指針return 0;//程序正常退出}
可以看到是未初始化的,放的隨機值
realloc
功能:內(nèi)存塊的擴容
參數(shù):第一個參數(shù)接收要擴容內(nèi)存塊的首地址,擴容后總字節(jié)大小(包括原來的字節(jié)大小)
頭文件:stdlib.h
返回值:
realloc函數(shù)使用
int main()
{int* p = (int*)malloc(40);//申請了40個字節(jié),強制轉(zhuǎn)化為int*類型指針if (p == NULL)//如果返回空指針的話,申請失敗{perror("malloc:");//打印錯誤信息return 1;//非正常退出}for (int i = 0; i < 10; i++)//循環(huán)打印擴容前的元素{*(p + i) = i;printf("%d ", *(p + i));}int* ptr = (int*)realloc(p, 80);//原空間夠用ptr==p,不夠用的話ptr存放新地址if (ptr != NULL)//擴容成功{p = ptr;//原空間夠用ptr==p,不夠用的話ptr存放新地址,重新將新地址給p}for (int i = 10; i < 20; i++)//擴容后新空間的{*(p + i) = i;printf("%d ", *(p + i));}free(p);p = NULL;return 0;
}
編譯運行
👀常見的動態(tài)內(nèi)存錯誤
1.對NULL指針的解引用操作
int main()
{int* p = (int*)malloc(1000);int i = 0;//if (p ==NULL)//{// return 1;//}for (i = 0; i < 250; i++){*(p + i) = i;}free(p);p = NULL;return 0;
}
當malloc申請內(nèi)存失敗,p=NULL,i=0,相當于給空指針解引用
解決辦法:對malloc函數(shù)返回值做出判斷
int main()
{int* p = (int*)malloc(1000);int i = 0;if (p ==NULL){return 1;}for (i = 0; i < 250; i++){*(p + i) = i;}free(p);p = NULL;return 0;
}
2. 對動態(tài)開辟空間越界訪問
int main()
{int* p = (int*)malloc(100);int i = 0;if (p ==NULL){return 1;}for (i = 0; i <=25; i++)//越界訪問{*(p + i) = i;}free(p);p = NULL;return 0;
}
編譯運行
解決方法:人為檢查是否越界
修改:
int main()
{int* p = (int*)malloc(100);int i = 0;if (p ==NULL){return 1;}for (i = 0; i <25; i++)//=25變成<25{*(p + i) = i;}free(p);p = NULL;return 0;
}
3.對非動態(tài)開辟內(nèi)存進行free
int main()
{int a = 10;int* p = &a;free(p);p = NULL;return 0;
}
編譯運行
解決方案:你別手賤(🙂)
4.使用free釋放一塊動態(tài)開辟內(nèi)存的一部分
int main()
{int* p = (int*)malloc(100);if (p == NULL){return 1;}int i = 0;for (i = 0; i < 10; i++){*p = i;p++;}free(p);p = NULL;return 0;}
編譯運行
解決方案:別改變p指向的地址,或者用一個指針記錄申請內(nèi)存的首地址
plan1:
int main()
{int* p = (int*)malloc(100);if (p == NULL){return 1;}int i = 0;for (i = 0; i < 10; i++){*(p+i)= i;printf("%d ", *(p + i));}free(p);p = NULL;return 0;}
plan2:
int main()
{int* p = (int*)malloc(100);int* q = p;if (p == NULL){return 1;}int i = 0;for (i = 0; i < 10; i++){*p= i;printf("%d ", *p);p++;}free(q);q = NULL;return 0;}
5.多次free已經(jīng)釋放的內(nèi)存
int main()
{int* p = malloc(40);if (p == NULL){return 1;}free(p);free(p);p = NULL;return 0;
}
編譯運行
解決方案:別多次free已經(jīng)釋放的內(nèi)存(滑稽)
6.動態(tài)開辟內(nèi)存忘記釋放
見上面
👀幾個經(jīng)典的筆試題
1
char *GetMemory(void)
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char *str = NULL;
str = GetMemory();
printf(str);
}
分析:定義一個char*str,讓他指向空,str接收GetMemory()函數(shù)返回來的地址,進入GetMemory()函數(shù),return p,只能把p[]的首地址傳回去,而p[]是局部變量,出GetMemory(),p[]銷毀,當你傳回去的時候,str接收的是野地址,str為野指針。打印不出來hello world
編譯運行:
2.
void GetMemory(char *p)
{
p = (char *)malloc(100);
}
void Test(void)
{
char *str = NULL;
GetMemory(str);
strcpy(str, "hello world");
printf(str);
}
分析:
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);
}
分析:
但是沒有free釋放內(nèi)存
運行編譯
4.
void main(void)
{
char *str = (char *) malloc(100);
strcpy(str, "hello");
free(str);
if(str != NULL)
{
strcpy(str, "world");
printf(str);
}
}
分析:
運行編譯
👀 柔性數(shù)組
也許你從來沒有聽說過柔性數(shù)組(flexible array)這個概念,但是它確實是存在的。 C99 中,結(jié)構(gòu)中的最
后一個元素允許是未知大小的數(shù)組,這就叫做『柔性數(shù)組』成員
柔性數(shù)組的特點
結(jié)構(gòu)中的柔性數(shù)組成員前面必須至少一個其他成員。sizeof 返回的這種結(jié)構(gòu)大小不包括柔性數(shù)組的內(nèi)存。
包含柔性數(shù)組成員的結(jié)構(gòu)用malloc ()函數(shù)進行內(nèi)存的動態(tài)分配,并且分配的內(nèi)存應該大于結(jié)構(gòu)的大小,以適應柔性數(shù)組的預期大小。
我們可以定義一個結(jié)構(gòu)體
struct pp {int a;int b[];};
int main()
{printf("%d", sizeof(struct pp));}
編譯運行
確實沒有包括柔性數(shù)組大小
柔性數(shù)組的使用
struct pp {int a;int b[];//柔性數(shù)組成員
};
int main()
{struct pp* p = (struct pp*)malloc(sizeof(struct pp) + 10 * sizeof(int));//malloc中第一個元素大小+柔性數(shù)組字節(jié)大小p->a = 4;//賦值for (int i = 0; i < 10; i++){p->b[i] = i;//賦值}for (int i = 0; i < 10; i++){printf("%d ", p->b[i]);//打印柔性數(shù)組}printf("%d ", p->a);free(p);//free掉malloc來的空間p = NULL;//p置為空指針}
我們能不能用指針代替那個柔性數(shù)組呢,我們可以將指針指向的那個地方malloc來使用
定義一個結(jié)構(gòu)體
struct pp {int a;int* p;
};
int main()
{struct pp* q = (struct pp*)malloc(sizeof(struct pp));//申請結(jié)構(gòu)體大小的內(nèi)存if (q == NULL)//判斷申請是否成功{return 1;//異常退出}q->p = (int*)malloc(10*sizeof(int));if (q->p == NULL)//判斷申請是否成功{return 1;//異常退出}q->a = 10;//賦值for (int i = 0; i < 10; i++){q->p[i]= i;賦值}printf("%d ", q->a);for (int i = 0; i < 10; i++){printf("%d ", q->p[i]);}free(q->p);//free掉p指針指向的另一塊申請空間的內(nèi)存q->p = NULL;//指針置空,防止野指針free(q);//free掉q指向的申請的內(nèi)存q = NULL;}
分析:
malloc過程
釋放過程
編譯運行
上述 代碼1 和 代碼2 可以完成同樣的功能,但是 方法1 的實現(xiàn)有兩個好處: 第一個好處是:方便內(nèi)存釋放
如果我們的代碼是在一個給別人用的函數(shù)中,你在里面做了二次內(nèi)存分配,并把整個結(jié)構(gòu)體返回給用戶。用戶調(diào)用free可以釋放結(jié)構(gòu)體,但是用戶并不知道這個結(jié)構(gòu)體內(nèi)的成員也需要free,所以你不能指望用戶來發(fā)現(xiàn)這個事。所以,如果我們把結(jié)構(gòu)體的內(nèi)存以及其成員要的內(nèi)存一次性分配好了,并返回給用戶一個結(jié)構(gòu)體指針,用戶做一次free就可以把所有的內(nèi)存也給釋放掉。
第二個好處是:這樣有利于訪問速度.
連續(xù)的內(nèi)存有益于提高訪問速度,也有益于減少內(nèi)存碎片,根據(jù)局部性原理,連續(xù)存放的數(shù)據(jù),cup從緩沖區(qū)讀取的快,緩存區(qū)從內(nèi)存中讀取的快。
總結(jié)
本片分享了四個動態(tài)內(nèi)存函數(shù),以及常見動態(tài)內(nèi)存錯誤,幾個經(jīng)典的筆試題,以及柔性數(shù)組的概念,如果你覺得對你有幫助的話,希望能留下你的點贊,關(guān)注加收藏,如果有不對的地方,可以私信我,謝謝各位佬們!!!