發生內存錯誤時間非常麻煩的事情。編譯器不能發現這些錯誤,只能在程序運行時才能捕捉到。常見的內存錯誤有三種,野指針、內存泄漏和內存覆蓋
一、野指針問題
野指針是指向了被釋放或沒有訪問權限的內存指針。
1、訪問權限
//野指針
int* p;
p = 10;
//報錯,使用了未初始化的變量p
2、指向被釋放的內存
二、內存泄漏
內存泄露(memory lock)是指由於疏忽或錯誤造成程序未能釋放已經不再使用的內存的情況。
1、內存使用完之後,沒有釋放;
2、對new或malloc出的指針進行重新賦值。
內存泄漏查詢方法
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
int main(){
_CrtSetBreakAlloc();
....................
_CrtDumpMemoryLeaks();
return 0;
}
詳解:
https://blog.csdn.net/daaikuaichuan/article/details/80874436
三、內存覆蓋
strcpy、memcpy、strcat容易發生內存覆蓋問題
1、strcpy函數
char* strcpy(char* dst, const char* src, int size) {
assert(src);
assert(dst);
if (dst <= src || dst > src + size) {
char* d = dst;
const char* s = src;
while (size--) {
*d++ = *s++;
}
}
else {//發生內存覆蓋,逆向複製
char* d = dst + size - 1;
const char* s = src + size - 1;
while (size--) {
*d-- = *s--;
}
}
return dst;
}
2、memcpy函數
void* memcpy(void* dst, const void* src, int size) {
if (src != nullptr || dst != nullptr) {
return nullptr;
}
char* d = (char*)dst;
char* s = (char*)src;
if (d <= s || d > s + size) {
while (size--) {
*d++ = *s++;
}
}
else {//發生內存覆蓋,逆向複製
d = d + size - 1;
s = s + size - 1;
while (size--) {
*d-- = *s--;
}
}
}
3、strcat函數
char* strcat(char* dst, const char* src) {
assert(src);
assert(dst);
char* p = dst;
while (*dst != '\0') {
dst++;
}
while (*dst++ = *src++ != '\0');
return p;
}
四、常見錯誤及對策
1、內存分配失敗
內存分配失敗卻使用了它
指針p爲函數參數,在函數入口處進行檢查:
assert(p != NULL);
new或malloc申請內存進行判斷:
if(p == NULL)
2、內存操作越界
常見的數組越界,發生在下標多‘1’或少‘1’;
指針超過變量範圍(野指針)
3、指針越界
4、空指針
常見對策
規則 | |
---|---|
規則1 | 用malloc或new申請內存之後,應該立即檢查指針值是否爲NULL。防止使用指針值爲NULL的內存。 |
規則2 | 不要忘記爲數組和動態內存賦初值。防止將未被初始化的內存作爲右值使用。 |
規則3 | 避免數組或指針的下標越界,特別要當心發生“多1”或者“少1”操作。 |
規則4 | 動態內存的申請與釋放必須配對,防止內存泄漏。 |
規則5 | 用free或delete釋放了內存之後,立即將指針設置爲NULL,防止產生“野指針”。 |
野指針與內存泄漏的關係
- 指針銷燬了,並不表示所指的空間也得到釋放了: 內存泄漏!
- 內存釋放了,並不代表指針被銷燬或者指向了NULL ;未初始化的指針(野指針)