【C】 42_記憶體操作經典問題分析 二

TianSong發表於2019-05-11

常見記憶體錯誤

  • 結構體成員指標未初始化
  • 結構體成員指標未分配足夠的記憶體
  • 記憶體分配成功,但並未初始化(尤其字串操作時)
  • 記憶體操作越界

例項分析: 常見記憶體錯誤 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 時,程式可能產生崩潰

以上內容參考狄泰軟體學院系列課程,請大家保護原創!

相關文章