網站首頁 編程語言 正文
一、為什么會存在動態內存
int data=20;//在棧空間上開辟4個字節空間
char ch[5]={0};//在棧開辟5個字節連續空間
上面展示的即為我們正常開辟固定的內存空間,它有兩個方面的特點
1.內存空間所占大小是固定的,不能改變的。
2.創建數組時,必須指明長度大小,在編譯時內存進行分配。
很顯然靜態分配內存分配在一些場景,就暴露出它的弊端。如果在開發之前,我們不知道空間的需求,我們有時只有在程序運行的時候才能知道自己所需要空間大小,這時候我們只能使用動態分配內存了。
二、動態內存函數
1.malloc和free
malloc函數的參數只有一個size_t size,向內存申請一塊連續可用的空間,有幾點需要注意
1.如果開辟成功的話,返回指向開辟好空間的指針
2.如果開辟失敗的話,則返回NULL,因此每次開辟空間之后,都要進行檢查
3.malloc函數未定義返回類型,一切由使用者自己使用
4.需引用stdlib.h頭文件
free函數是和malloc配套使用的,每次在堆開辟動態空間后,程序結束之前,必須進行空間釋放,不然會出現動態空間泄露,在使用free時,仍需要注意幾點
1.如果指針指向的空間不是動態開辟的,不能用free進行釋放
2.如果指針指向的是null指針,則free函數什么事都不做
3.free不能多次使用
4.需引用stdlib.h頭文件
代碼如下(示例):
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
int main()
{
int* src = NULL;
src = (int*)malloc(40);//開辟40字節動態內存
if (src == NULL)
{
printf("%s", strerror(errno));
return 1;
}
free(src);//進行動態內存釋放
src = NULL;
return 0;
}
相信有人會問,不是已經對動態內存進行釋放,為什么還要令指針等于NULL,我們調試一把。
這里我們可以發現,雖然動態內存進行free釋放,但指針仍然指向被釋放的動態內存的地址,如果不置空,就會造成野指針,非法訪問的問題。
2.calloc
calloc和malloc最大的區別就是,malloc只負責對內存進行動態開辟,但calloc不僅開辟,還進行初始化。
代碼如下(示例):
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
int main()
{
int* src = (int*)calloc(10, sizeof(int));
if (src == NULL)
{
printf("%s", strerror(errno));
return 1;
}
free(src);//進行動態內存釋放
src = NULL;
return 0;
}
我們調試一把可以發現,calloc在開辟空間時同時進行了初始化。所以如何我們對申請的內存空間的內容要求初始化,那么可以很方便的使用calloc函數來完成任務。
3.realloc
當我們一次開辟動態內存不夠大的時候,realloc讓動態內存更加的靈活。realloc幾個參數:
1.第一個參數為要調整內存的地址
2.調整后大小
3.調整后內存的起始位置
為什么還要返回調整后內存的地址,不是直接就開辟好了嗎?其實reallloc函數在開辟時有以下兩種情況:
1.原來的內存之后空間是足夠的,則直接開辟
2.原來的內存之后空間不夠用。
我們畫圖刨析一下
情況1:直接追加空間,原來數據不變
情況2:沒有足夠的空間,在堆上找一個大小合適的連續空間。所以函數返回的是一個新的內存地址。
代碼如下(示例)
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
int main()
{
int* src = NULL;
src = (int*)malloc(40);//開辟40字節動態內存
if (src == NULL)
{
printf("%s", strerror(errno));
return 1;
}
src = realloc(src, 80);
if (src == NULL)
{
printf("%s", strerror(errno));
return 1;
}
free(src);//進行動態內存釋放
src = NULL;
return 0;
}
三、動態內存函數常見錯誤
1.動態內存越界訪問
void test1()
{
int* src = (int*)malloc(20);
if (NULL == src)
{
return 1;
}
int i = 0;
for (i = 0; i < 6; i++)
{
(*src+i)=i
}
free(src);
src = NULL;
}
在這里我們malloc只開辟了20個字節,但(*src+5)造成了越界訪問
2.對NULL指針進行解引用操作
void test2()
{
int* src = (int*)malloc(INT_MAX);//此處INT_MAX為int的最大值
*src = 10;//如果src是NULL時,無法解引用
free(src);
return 0;
}
這里未對開辟的動態內存空間進行是否為空的判斷,當為空時,解引用就會出現錯誤。
3.使用free釋放一塊動態開辟內存的一部分
void test3()
{
int* src = (int*)malloc(40);
int i=0;
for(i=0;i<6;i++)
{
*(src+i)=i;
src++;
}
free(src);//此時src不指向起始位置
}
因為指針指向的地址發生變化,不在指向起始未知,進行free釋放是非常危險的。
4.對靜態內存進行free釋放
void test4()
{
int a = 20;
int* src = &a;
free(src);
}
5.對同一內存空間多次釋放
void test5()
{
int* src = (int*)malloc(40);
free(src);
free(src);//多次釋放
}
第一個free已經將堆空間的動態內存進行釋放,此時src已經是一個野指針,在進行釋放是十分危險的。
6.動態開辟空間忘記釋放
void test6()
{
int* src = (int*)malloc(40);
if (src != NULL)
{
}
while (1);
}
在開辟動態內存之后,一直進行while循環,為進行free釋放,會造成內存泄漏。
四、經典筆試題
1.筆試1
void test(char* src)
{
src = (char*)malloc(30);
}
int main()
{
char* src = NULL;
test(src);
strcpy(src, "wo yao jin da chang");
printf(src);
free(src);
}
這里會輸出wo yao jin da chang 嗎?
這里很明顯,src仍然是NULL,所以無法輸出wo yao jin da chang
2.筆試2
char* test()
{
char arr[] = "wo yao jin da chang";
return arr;
}
int main()
{
char* src = NULL;
src = tset();
printf(src);
return 0;
}
這里會輸出wo yao jin da chang 嗎?
這里test函數確實把字符串地址傳給了src,但是字符串是局部變量,當函數執行完之后,就銷毀了,所以src輸出的內容是隨機的。
3.筆試3
void test()
{
char* src = (char*)malloc(50);
if (src != NULL)
{
strcpy(src, "wo yao jin da chang");
}
free(src);
if (src != NULL)
{
strcpy(src, "taijuanlebujinle");
printf(src);
}
}
這里會輸出taijuanlebujinle 嗎?
這里對動態內存釋放后,繼續進行賦值,對野指針進行了訪問是錯誤的。
總結
看到這里大家對動態內存管理已經有了一定的認識,應該特別注意這幾點,在進行動態內存開辟之后進行判斷是否為空,使用完后進行free釋放,并且置空,防止動態內存泄露,只要記住這幾點基本就可以很好的使用動態內存了。
原文鏈接:https://blog.csdn.net/buhuisuanfa/article/details/125768729
相關推薦
- 2022-09-15 C#中DateTime的時間加減法操作小結_C#教程
- 2022-03-26 Qt?多語言程序設計的實現_C 語言
- 2022-10-04 python編寫一個GUI倒計時器_python
- 2022-01-31 微信小程序獲取二維碼中URL中帶的參數
- 2022-05-13 eslint-disable-next-line no-prototype-builtins
- 2022-05-15 Python+Selenium實現讀取網易郵箱驗證碼_python
- 2023-01-13 Android?Parcleable接口的調用源碼層分析_Android
- 2022-05-17 C++?std::shared_mutex讀寫鎖的使用_C 語言
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支