1

常见内存错误

  • 结构体成员指针未初始化
  • 结构体成员指针未分配足够的内存
  • 内存分配成功,但并未初始化(尤其字符串操作时)
  • 内存操作越界

实例分析: 常见内存错误 1

#include <stdio.h>
#include <malloc.h>

void test(int* p, int size)
{
    int i = 0;
    
    for(i=0; i<size; i++)
    {
        printf("%d\n", 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\n", 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\n", 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\n", "D.T.Software");
    
    for(i=0; i<size; i++)
    {
        printf("%d\n", 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 时,程序可能产生崩溃

以上内容参考狄泰软件学院系列课程,请大家保护原创!


TianSong
734 声望138 粉丝

阿里山神木的种子在3000年前已经埋下,今天不过是看到当年注定的结果,为了未来的自己,今天就埋下一颗好种子吧