常見記憶體錯誤
- 結構體成員指標未初始化
- 結構體成員指標未分配足夠的記憶體
- 記憶體分配成功,但並未初始化(尤其字串操作時)
- 記憶體操作越界
例項分析: 常見記憶體錯誤 1
#include <stdio.h>
#include <malloc.h>
void test(int* p, int size)
{
int i = 0;
for(i=0; i<size; i++)
{
printf("%d
", p[i]);
}
free(p);
}
void func(unsigned int size)
{
int* p = (int*)malloc(size * sizeof(int));
int i = 0;
if( size % 2 != 0 )
{
return; // 此處將導致記憶體洩漏
}
for(i=0; i<size; i++)
{
p[i] = i;
printf("%d
", p[i]);
}
free(p);
}
int main()
{
int* p = (int*)malloc(5 * sizeof(int));
test(p, 5);
free(p); // 指標 p 兩次釋放,執行奔潰
func(9);
func(10);
return 0;
}
輸出:
0
0
0
0
0
*** glibc detected *** ./a.out: double free or corruption (fasttop): 0x08d29008 ***
======= Backtrace: =========
/lib/libc.so.6(+0x6c0c1)[0x63a0c1]
/lib/libc.so.6(+0x6d930)[0x63b930]
/lib/libc.so.6(cfree+0x6d)[0x63ea1d]
./a.out[0x804851f]
/lib/libc.so.6(__libc_start_main+0xe7)[0x5e4ce7]
./a.out[0x8048391]
======= Memory map: ========
00364000-00365000 r-xp 00000000 00:00 0 [vdso]
005ce000-00725000 r-xp 00000000 08:02 4645 /lib/libc-2.12.1.so
00725000-00727000 r--p 00157000 08:02 4645 /lib/libc-2.12.1.so
00727000-00728000 rw-p 00159000 08:02 4645 /lib/libc-2.12.1.so
00728000-0072b000 rw-p 00000000 00:00 0
00a2f000-00a49000 r-xp 00000000 08:02 102 /lib/libgcc_s.so.1
00a49000-00a4a000 r--p 00019000 08:02 102 /lib/libgcc_s.so.1
00a4a000-00a4b000 rw-p 0001a000 08:02 102 /lib/libgcc_s.so.1
00ee5000-00f01000 r-xp 00000000 08:02 4629 /lib/ld-2.12.1.so
00f01000-00f02000 r--p 0001b000 08:02 4629 /lib/ld-2.12.1.so
00f02000-00f03000 rw-p 0001c000 08:02 4629 /lib/ld-2.12.1.so
08048000-08049000 r-xp 00000000 08:05 524332 /home/delphi/桌面/a.out
08049000-0804a000 r--p 00000000 08:05 524332 /home/delphi/桌面/a.out
0804a000-0804b000 rw-p 00001000 08:05 524332 /home/delphi/桌面/a.out
08d29000-08d4a000 rw-p 00000000 00:00 0 [heap]
b7700000-b7721000 rw-p 00000000 00:00 0
b7721000-b7800000 ---p 00000000 00:00 0
b78ad000-b78ae000 rw-p 00000000 00:00 0
b78bb000-b78be000 rw-p 00000000 00:00 0
bfed0000-bfef1000 rw-p 00000000 00:00 0 [stack]
已放棄
例項分析: 常見記憶體錯誤 2
#include <stdio.h>
#include <malloc.h>
struct Demo
{
char* p;
};
int main()
{
struct Demo d1; // 結構體成員指標未初始化,造成野指標
struct Demo d2; // 結構體成員指標未初始化,造成野指標
char i = 0;
for(i=`a`; i<`z`; i++)
{
d1.p[i] = 0; // 野指標被使用
}
d2.p = (char*)calloc(5, sizeof(char));
printf("%s
", d2.p);
for(i=`a`; i<`z`; i++)
{
d2.p[i] = i; // 記憶體越界
}
free(d2.p);
return 0;
}
輸出:
段錯誤
記憶體操作的交通規則
- 動態記憶體申請之後,應該立即檢查指標值是否為 NULL, 防止使用 NULL 指標。
int* p = (int*)malloc(56);
if(p != NULL)
{
// Do something here!
}
free(p);
- free 指標之後必須立即賦值為 NULL
int* p = (int*)malloc(20);
free(p);
p = NULL;
//...
if( p != NULL )
{
// Do something here
}
- 任何與記憶體相關的函式都必須帶長度資訊,防止記憶體越界
void print(int* p, int size)
{
int i = 0;
char buf[128] = {0};
snprintf(buf, sizeof(buf), "%s
", "D.T.Software");
for(i=0; i<size; i++)
{
printf("%d
", p[i]);
}
}
malloc 操作和 free 操作必須匹配,防止記憶體洩漏和多次釋放。
void func()
{
int* p = (int*)malloc(20);
free(p);
}
int main()
{
int* p = (int*)malloc(40);
func();
free(p);
return 0;
}
記憶體洩漏對於需要長時間執行的裝置是致命的 bug,但同時記憶體洩漏問題是不易被查詢的。在程式設計時,儘量保證同一個函式中申請資源,同一個函式中釋放資源。
小結
記憶體錯誤的本質源於指標儲存的地址為非法值
- 指標變數未初始化,儲存隨機值
- 指標運算導致記憶體越界
記憶體洩漏源於 malloc 和 free 不匹配
- 當 malloc 次數多餘 free 時,產生記憶體洩漏
- 當 malloc 次數少於 free 時,程式可能產生崩潰
以上內容參考狄泰軟體學院系列課程,請大家保護原創!