目錄
動態(tài)內(nèi)存存在的意義
動態(tài)內(nèi)存函數(shù)的介紹
malloc和free
calloc
realloc
常見的動態(tài)內(nèi)存錯誤
對NULL指針解引用操作
對動態(tài)開辟的空間的越界訪問
對非動態(tài)開辟內(nèi)存使用free釋放
使用free釋放一塊動態(tài)開辟內(nèi)存的一部分
對同一塊內(nèi)存多次釋放
動態(tài)開辟內(nèi)存忘記釋放
經(jīng)典的筆試題
C/C++程序的內(nèi)存開辟
柔性數(shù)組?
柔性數(shù)組的特點(diǎn)
柔性數(shù)組的使用
柔性數(shù)組的優(yōu)點(diǎn)
動態(tài)內(nèi)存存在的意義
int a = 10;
int arr[10] = { 0 };
上面變量開辟的空間有兩個特點(diǎn):
空間開辟的大小是固定的
數(shù)組在聲明的時候,必須指定數(shù)組的長度,它需要的內(nèi)存在編譯時分配
但是,我們有的時候空間的需要大小在程序運(yùn)行的時候才知道,數(shù)組的編譯是開辟空間的方式就不能滿足了。這時候就需要動態(tài)內(nèi)存開辟了。
動態(tài)內(nèi)存函數(shù)的介紹
malloc和free
void* malloc (size_t size)
?malloc函數(shù)向內(nèi)存申請一塊連續(xù)可用的空間,返回指向這塊空間的指針。
注意:
如果開辟成功,則返回一個指向這塊空間的指針。、
如果開辟失敗,則返回一個空指針,使用malloc的返回值要做檢查
返回值的類型是void*類型,malloc不知道開辟空間的類型,需要使用者自己來決定。
如果size為0,malloc的行為是屬于標(biāo)準(zhǔn)未定義的
C語言還提供了另一個函數(shù)free,是用來釋放動態(tài)內(nèi)存的。
void free (void* ptr)
?free函數(shù)是用來釋放動態(tài)開辟的內(nèi)存
如果參數(shù)ptr指向的不是動態(tài)開辟的空間,這種行為是標(biāo)準(zhǔn)未定義的。
如果參數(shù)ptr是NULL指針,則函數(shù)什么事都不會做
malloc和free的聲明都在stdlib.h中
舉個栗子:
要注意把ptr變?yōu)镹ULL,不然它就是野指針了
#include <stdio.h>
#include <stdlib.h>
int main()
{
int arr[10] = { 0 };
int* ptr = (int*)malloc(40);
if (ptr == NULL)
return 1;
for (int i = 0; i < 20; i++)
{
*(ptr + i) = i;
}
free(ptr); //釋放空間
ptr = NULL;//把ptr變?yōu)镹ULL,不然他是野指針
return 0;
}
calloc
void* calloc (size_t num, size_t size)
?calloc將num個大小為size的元素初始化為0
與malloc的區(qū)別在于calloc會在返回地址前將申請的空間全部初始化為0
int main()
{
int* ptr = (int*)calloc(10, sizeof(int));
if (ptr == NULL)
return 1;
for (int i = 0; i < 10; i++)
{
printf("%d\n",*(ptr + i));
}
free(ptr);
ptr = NULL;
return 0;
}
realloc
void* realloc (void* ptr, size_t size)
?realloc的出現(xiàn)可以讓動態(tài)內(nèi)存管理變得更加靈活。有時我們會發(fā)現(xiàn)過去申請的空間太小了,又有時覺得太大了。為了合理管理內(nèi)存,我們一定會對內(nèi)存的大小進(jìn)行調(diào)整。realloc就可以做做到對動態(tài)開辟內(nèi)存大小地調(diào)整。
ptr是要調(diào)整內(nèi)存地地址
size是調(diào)整后的大小
返回值為調(diào)整之后的內(nèi)存起始位置
這個函數(shù)調(diào)整原內(nèi)存空間大小的基礎(chǔ)上,會將原空間上的數(shù)據(jù)移動到新的空間
realloc在調(diào)整原內(nèi)存空間有兩種情況:
1 原有空間之后有足夠大的空間
2 原有空間之后沒有足夠大的空間
情況一:要擴(kuò)展內(nèi)存的時候就直接在原有內(nèi)存后面追加空間,原來空間的數(shù)據(jù)不發(fā)生變化。
情況二:原有空間之后沒有足夠的空間時,會在對堆上找另一塊空間來使用,函數(shù)返回的就是這個新的內(nèi)存地址。?
?栗子:
int main()
{
int* p = (int*)malloc(40);
if (p == NULL)
return 1;
for (int i = 0; i < 10; i++)
{
*(p + i) = i;
}
int* ptr = (int*)realloc(p, 50);
if (ptr == NULL)
return 1;
p = ptr;
ptr = NULL;
for (int i = 0; i < 20; i++)
{
printf("%d ", *(p + i));
}
free(p);
p = NULL;
return 0;
}
常見的動態(tài)內(nèi)存錯誤
對NULL指針解引用操作
void test()
{
int *p = (int *)malloc(INT_MAX/4);
*p = 20;//如果p的值是NULL,就會有問題
free(p);
}
這里沒有對p進(jìn)行判斷,如果malloc申請空間失敗的話,他就會為空指針,對空指針解引用就會出現(xiàn)問題。
對動態(tài)開辟的空間的越界訪問
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;//當(dāng)i是10的時候越界訪問
}
free(p);
}
觀察代碼我們發(fā)現(xiàn),當(dāng)i等于10時,會越界訪問,導(dǎo)致不可控的因素。
對非動態(tài)開辟內(nèi)存使用free釋放
void test()
{
int a = 10;
int *p = &a;
free(p);
}
使用free釋放一塊動態(tài)開辟內(nèi)存的一部分
void test()
{
int *p = (int *)malloc(100);
p++;
free(p);//p不再指向動態(tài)內(nèi)存的起始位置
}
p指針移動后釋放會導(dǎo)致一部分內(nèi)存無法釋放
對同一塊內(nèi)存多次釋放
void test()
{
int *p = (int *)malloc(100);
free(p);
free(p);//重復(fù)釋放
}
動態(tài)開辟內(nèi)存忘記釋放
void test()
{
int *p = (int *)malloc(100);
if(NULL != p)
{
*p = 20;
}
}
int main()
{
test();
while(1);
}
忘記釋放動態(tài)開辟的內(nèi)存這樣會導(dǎo)致內(nèi)存泄露,使這部分內(nèi)存無法再使用
經(jīng)典的筆試題
void GetMemory(char *p)
{
p = (char *)malloc(100);
}
void Test(void)
{
char *str = NULL;
GetMemory(str);
strcpy(str, "hello world");
printf(str);
}
這段代碼里面的str是傳值調(diào)用,這函數(shù)將str重新拷貝了一份,函數(shù)接受這個函數(shù)就銷毀了,str還是NULL,則strcpy不能使用,printf也打印不出
char *GetMemory(void)
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char *str = NULL;
str = GetMemory();
printf(str);
}
這里返回的p是野指針,因?yàn)槌隽撕瘮?shù)p開辟的空間就銷毀了,p指向的內(nèi)容是未知的,打印出來的內(nèi)容也是未知。
void GetMemory(char **p, int num)
{
*p = (char *)malloc(num);
}
void Test(void)
{
char *str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str);
}
這里雖然是傳址調(diào)用,但是到最后它沒有將開辟的空間釋放掉,導(dǎo)致內(nèi)存泄露。
void Test(void)
{
char *str = (char *) malloc(100);
strcpy(str, "hello");
free(str);
if(str != NULL)
{
strcpy(str, "world");
printf(str);
}
}
這里是提前釋放了空間,讓后面的拷貝的地址后沒有地方被他們存放
C/C++程序的內(nèi)存開辟
內(nèi)存分配的區(qū)域:
?堆區(qū)(heap):一般由程序員分配釋放, 若程序員不釋放,程序結(jié)束時可能由OS回收 。
柔性數(shù)組?
struct st_type
{
int i;
int a[0];//柔性數(shù)組成員
};
struct st_type
{
int i;
int a[];//柔性數(shù)組成員
};
有些編譯器可以使用第一種方法,有些可以使用第二種方法
柔性數(shù)組的特點(diǎn)
結(jié)構(gòu)體中的柔性成員前面至少得有一個其他的成員
sizeof求這種結(jié)構(gòu)的大小不會包括柔性數(shù)組
包括柔性數(shù)組成員的結(jié)構(gòu)用malloc進(jìn)行動態(tài)內(nèi)存分配的時候,分配的內(nèi)存應(yīng)該大于結(jié)構(gòu)體的大小,以便適用柔性數(shù)組的預(yù)期大小
typedef struct st_type
{
int i;
int a[0];//柔性數(shù)組成員
}type_a;
printf("%d\n", sizeof(type_a));//輸出的是4
柔性數(shù)組的使用
typedef struct st_type
{
int i;
int a[0];//柔性數(shù)組成員
}type_a;
int i = 0;
type_a *p = (type_a*)malloc(sizeof(type_a)+100*sizeof(int));
//業(yè)務(wù)處理
p->i = 100;
for(i=0; i<100; i++)
{
p->a[i] = i;
}
free(p);
這個柔性數(shù)組,相當(dāng)于獲得了100個整型的連續(xù)空間文章來源:http://www.zghlxwxcb.cn/news/detail-595598.html
柔性數(shù)組的優(yōu)點(diǎn)
上面的代碼其實(shí)也可以這樣設(shè)計(jì):文章來源地址http://www.zghlxwxcb.cn/news/detail-595598.html
typedef struct st_type
{
int i;
int* p_a;
}type_a;
type_a* p = (type_a*)malloc(sizeof(type_a));
p->i = 100;
p->p_a = (int*)malloc(p->i * sizeof(int));
//業(yè)務(wù)處理
for (i = 0; i < 100; i++)
{
p->p_a[i] = i;
}
//釋放空間
free(p->p_a);
p->p_a = NULL;
free(p);
p = NULL;
到了這里,關(guān)于一文帶你了解動態(tài)內(nèi)存管理的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!