
為什么存在動態記憶體分配
在C語言中我們常見的記憶體開辟方式有
int n = 10;//在堆疊空間上開辟四個位元組的空間
int arr[10] = { 0 };//在堆疊空間上開辟40個位元組連續空間
但是上述開辟空間的方式存在以下特點:
1.開辟空間的大小是連續的,
2.開辟的空間大小是固定的, 但是對于空間的需求,不僅僅只需要上述的情況,有時候我們需要的空間大小在程式運行的時候才能知道,那陣列的編譯時開辟空間的方式就不能滿足了,這個時候就需要動態記憶體開辟了,
動態記憶體函式
malloc函式
C語言提供了一個動態記憶體開辟的malloc函式,
void* malloc(size_t size);
//回傳值的型別是`void*`,所以使用者可以根據自己的需求來決定所開辟空間的型別,
//size為要開辟空間的大小單位為位元組
這個函式在堆區申請一塊連續可用的空間,
1.若開辟空間失敗,則回傳一個
NULL指標,
2.若開辟成功,則回傳一個指向開辟好的空間的指標, 因此,在應用malloc函式開辟空間時,我們需要對它的回傳值進行檢查,避免開辟空間失敗的情況,
free函式
free函式用來釋放動態開辟的記憶體,動態開辟的記憶體一定要主動釋放,否則會出現記憶體泄露的問題,
注:
如果指標p指向的空間不是動態開辟的,那free函式的行為是未定義的,
如果指標是NULL指標,則函式什么事情都不用做,
malloc函式與free函式的應用
int main()
{
int arr[10];//這是在堆疊區開辟的記憶體空間
int* p = (int*)malloc(sizeof(int)*10);//動態記憶體開辟的//malloc申請連續可用的空間,并回傳指向該空間的指標
if (p == NULL)//判斷是否開辟成功
{
perror("main");
return 1;
}
int i = 0;
for (i = 0; i < 10; i++)
{
*(p + i) = i;
}
for (i = 0; i < 10; i++)
{
printf("%d ", p[i]);
}
//回收空間
free(p);//只能釋放動態開辟的空間和malloc成對出現
p = NULL;//將p置為NULL指標的目的是為了防止被釋放后的非法訪問
return 0;
}
calloc函式
calloc函式也是用來動態記憶體分配,原形如下:
void* calloc(size_t num,size_t size);
函式的功能是開辟num個空間大小為szie的空間,并會把空間的每個位元組初始化為0;
函式的應用:
#include<stdio.h>
int main()
{
int* p = (int*)calloc(10, sizeof(int));
if(NUll == p)
{
perror("main");
return 1;
}
//使用空間
//釋放空間
free(p);
p = NULL;
return 0 ;
}
realloc函式
在動態開辟記憶體時有時候我們會發現開辟的空間太小或者太大,為了合理的使用記憶體,就可以使用realloc函式對開辟的記憶體空間進行調整,
realloc函式的原型如下:
void* realloc(void* ptr,size_t size);
//其中ptr為要調整的記憶體地址 size為調整之后的空間大小 回傳值為記憶體的起始地址
開辟空間時具有以下兩種情況:

常見的動態記憶體錯誤
1、對NULL指標的解參考操作

2、對動態開辟的記憶體的越界訪問

3、使用free釋放非動態開辟的空間

使用free釋放動態記憶體中的一部分

5、對同一個動態記憶體地址多次釋放

6、動態開辟的空間忘記釋放
動態開辟的記憶體有兩種釋放方式:
1.主動free
2.程式結束
因此如果忘記釋放空間會導致記憶體泄露的問題、因此要注意主動free掉動態開辟的空間,

經典筆試題
void GetMemory(char* p)
{
p = (char*)malloc(100);
}
void Test(void)
{
char* str = NULL;
str = GetMemory(str);
strcpy(str, "hello world");
printf(str);//printf接收的就是字串的地址
}
int main()
{
Test();
return 0;
}
str傳給GetMemory函式是傳值,所以形參p只是臨時拷貝,在函式內申請的空間,存在p中,不會影響外邊的str所以當函式回傳之后 ,str依然是NULL所以strcpy會失敗
當函式回傳值時,p會銷毀,會出現記憶體泄露
改正:放回p的地址,并主動free
char* GetMemory(char* p)
{
p = (char*)malloc(100);
return p;
}
void Test(void)
{
char* str = NULL;
str = GetMemory(str);
strcpy(str, "hello world");
printf(str);
free(str);
str = NULL;
}
int main()
{
Test();
return 0;
}
char *GetMemory(void)
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char *str = NULL;
str = GetMemory();
printf(str);
}
題中p是在堆疊區開辟的記憶體空間,當GetMemory函式結束時就會被釋放因此導致非法訪問,
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掉動態開辟的記憶體
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);
}
int main()
{
Test();
return 0;
}
void Test(void)
{
char *str = (char *) malloc(100);
strcpy(str, "hello");
free(str);
if(str != NULL)
{
strcpy(str, "world");
printf(str);
}
}
題中的問題是,釋放完動態開辟的記憶體后,再次訪問該記憶體,導致非法訪問,
改正:每次釋放記憶體之后將指標置為NULL指標
void Test(void)
{
char* str = (char*)malloc(100);
strcpy(str, "hello");
free(str);
str = NULL;
if (str != NULL)
{
strcpy(str, "world");
printf(str);
}
}
int main()
{
Test();
return 0;
}
C/C++程式的記憶體開辟

堆疊區(satck):在執行函式時,函式的區域變數的存盤空間就是在堆疊上創立,函式執行結束時這些存盤單元就會被釋放,堆疊區主要存放運行函式而分配的區域變量,函式引數,放回資料,回傳地址等,
堆區(heap):動態開辟的存盤空間就是在堆區開辟的,堆區一般又程式員分配、釋放,若程式員不釋放,程式結束時會被作業系統回收,
資料段(靜態區):(static)存放全域變數和靜態資料,程式結束后被系統釋放,
代碼段:存放函式體的二進制代碼,
柔性陣列
定義:在C99中,結構體中的最后一個元素允許是未知大小的陣列,這就叫做【柔性陣列】成員,例如:
struct MyStruct
{
int n;
int arr[];//該陣列的大小是未知
};
柔性陣列的特點
1、結構體中的柔性陣列成員前至少有一個其他成員,
2、sizeof回傳的這種結構體大小不包括柔性陣列的大小,(如上例中的結構體大小就是整型資料n的大小,其大小為4個位元組,)
3、包含柔性陣列的結構體成員用malloc函式進行記憶體的動態分配,通常分配的記憶體大小應該大于結構體的大小,
分配方式如下:
struct MyStruct
{
int n;
int arr[0];
};
int main()
{
//期望arr的大小是10int后面陣列的大小是可控的
struct MyStruct* ps = malloc(sizeof(struct MyStruct) + 10 * sizeof(int));
//使用空間
//釋放空間
free(ps);
ps = NULL;
return 0;
}

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/289583.html
標籤:其他
上一篇:?演算法入門?《線性迭代》簡單03 —— LeetCode 412. Fizz Buzz
下一篇:gcc/g++超詳細上手教程
