动态内存分配

动态分配内存的意义

定义数组的长度的时候,必须指定长度,这是在编译期间就要确定的。

当我们无法在编译期间确定到底需要多大的内存块,此时就无法定义数组的大小:

  • 实际使用的元素数量超过了声明的长度,程序无法处理。
  • 如果程序实际使用的元素数量较少,巨型数组会造成内存空间浪费。

此时就需要在运行的时候根据实际的情况(比如根据输入的数据的大小),来动态的申请内存空间,然后让指针指向这块新申请的内存。

malloc和free

malloc和free是库函数,不是系统调用

#include "stdlib.h"

void * malloc(size_t size);
void free(void* pointer);

关于malloc

  • malloc所分配的是一块连续的内存,参数size是所分配的内存字节数。
  • malloc的返回值是void* ,具体使用的时候需要做强制类型转换。
  • 当请求的动态内存无法满足的时候,malloc返回NULL,对每个从malloc返回指针都进行检查,确保它不是NULLL。
  • malloc申请的动态内存中的数据是随机值,不会被初始化为0。
  • malloc 实际分配的内存可能有会比请求的多。

关于free

  • free的参数要么是NULL,要么是从malloc、callocrealloc返回的值。
  • free用于将申请的动态内存归还给系统。
  • 当 free 的参数为 NULL 时,函数直接返回。

calloc与realloc

#include "stdlib.h"

void* `calloc`(size_t num,size_t size);
void* `realloc`(void* pointer,size_t new_size);

关于calloc

  • calloc函数的参数包含了所需元素的数量以及每个元素的字节数,根据这些值可以计算出一共所需的内存大小。
  • calloc会将申请的内存空间初始化为0。
  • 如果指向把值存储到数组中,这种操作会浪费一定的时间。

关于realloc

  • realloc用于修改原先已经分配的内存块大小。
  • realloc的第一个参数pointer为NULL时,realloc相当于malloc。
  • realloc用于缩小一个内存块时,该内存块的尾部的部分将会被拿掉。realloc用于扩大内存块时,原有内容依然保留,新增加的内存块添加到原先内存块的后面,新内存并未以任何方式初始化。
  • 如果原先的内存块无法改变大小,realloc将会分配一个正确大小的内存,并把原有的内容拷贝到新的内存块上,因此realloc之后就不能使用指向旧内存的指针,而应该使用realloc返回的指针。

常见的动态内存分配错误

对NULL指针解引用操作

int main(void)
{
    int *p = (int *)malloc(sizeof(int));

    *p = 20;    //这里没有判断返回值
                //如果返回值为NULL则会出现错误
    free(p);

    system("pause");
    return 0;
}

对动态开辟的空间越界访问

int main(void)
{   
    int *p = (int *)malloc(10 * sizeof(int));
    if (p == NULL)
    {
        exit(EXIT_FAILURE);
    }

    int i = 0;
    for (i = 0; i <= 10; i++)  // 访问越界
    {
        p[i] = i;
    }

    free(p);
    p = NULL;
    
    system("pause");
    return 0;
}

对非动态开辟的内存使用free释放

int main(void)
{
    int a = 10;
    int *p = &a;
    free(p);  // p不是动态申请的内存

    system("pause");
    return 0;
}

使用free释放一块动态开辟内存的一部分

int main(void)
{
    int *p = (int *)malloc(10 * sizeof(int));

    if (p == NULL)
    {
        exit(EXIT_FAILURE);
    }

    p++;
    free(p);  // 释放部分动态申请的内存

    system("pause");
    return 0;
}

对同一块内存多次释放

int main(void)
{
    int *p = (int *)malloc(sizeof(int));

    if (p == NULL)
    {
        exit(EXIT_FAILURE);
    }

    free(p);
    free(p);  // 第二次释放

    system("pause");
    return 0;
}

动态开辟的内存忘记释放(内存泄漏)

int main(void)
{
    int *p = (int *)malloc(sizeof(int) * 10);

    if (p == NULL)
    {
        exit(EXIT_FAILURE);
    }

    system("pause");
    return 0;
}

编写内存泄漏检查模块

头文件 mleak.h

#pragma once

#include "stdio.h"
#include "malloc.h"

#define MALLOC(n) mallocEx(n,__FILE__,__LINE__)
#define FREE(p)   freeEx(p)

void *mallocEx(size_t n, const char *file, const int line);
void freeEx(void *p);
void PRINT_LEAK_INFO();

实现 mleak.c

#include "mleak.h"

#define MAXSIZE 256
typedef struct
{
    void * pointer;
    int size;
    const char* file;
    int line;   
}MallocItem;

static MallocItem g_record[MAXSIZE];

void *mallocEx(size_t n, const char *file, const int line)
{
    void * p = malloc(n);
    int i = 0;
    if (p != NULL)
    {
        for (i = 0; i < MAXSIZE; i++)
        {
            if (g_record[i].pointer == NULL)
            {
                g_record[i].pointer = p;
                g_record[i].size = n;
                g_record[i].file = file;
                g_record[i].line = line;
                break;
            }
        }
    }

    return p;
}

void freeEx(void *p)
{
    if (p != NULL)
    {
        int i = 0;
        for (i = 0; i < MAXSIZE; i++)
        {
            if (g_record[i].pointer == p)
            {
                g_record[i].pointer = NULL;
                g_record[i].size = 0;
                g_record[i].file = NULL;
                g_record[i].line = 0;
                break;
            }
        }
    }
}

void PRINT_LEAK_INFO()
{   int i = 0;
    printf("Potenital Memory Leak Info:\n");
    for (i=0; i < MAXSIZE; i++)
    {
        if (g_record[i].pointer != NULL)
        {
            printf("Address:%p, size:%d, Location:%s:%d\n",
                g_record[i].pointer,
                g_record[i].size,
                g_record[i].file,
                g_record[i].line);
        }
    }
}

测试

#include "stdio.h"
#include "stdlib.h"
#include "mleak.h"

void f()
{
    MALLOC(100);
}

int main()
{
    int* p = (int*)MALLOC(3 * sizeof(int));

    f();

    p[0] = 1;
    p[1] = 2;
    p[2] = 3;

    FREE(p);

    PRINT_LEAK_INFO();

    return 0;
}

结果:
819930-20190915103554148-1017622468.png

转载于:https://www.cnblogs.com/chay/p/11521379.html

你可能感兴趣的:(动态内存分配)