动态内存管理

本文详细介绍了C/C++中动态内存分配的重要性,包括malloc、calloc、realloc函数的使用,并列举了动态内存管理中常见的错误,如NULL指针解引用、越界访问、释放非动态内存等,同时探讨了柔性数组的概念、特点和优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

1.为什么要存在动态内存分配

2.动态内存函数的介绍

2.1malloc和free

2.2calloc

2.3realloc

3.常见动态内存错误

3.1对NULL指针的解引用操作

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

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

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

3.5对同一块动态内存多次释放

3.6动态内存忘记释放(内存泄漏)

4.C/C++程序内存开辟

5.柔性数组

5.1柔性数组的特点:

 5.2柔性数组的使用

5.3柔性数组的优势


1.为什么要存在动态内存分配

int val = 20;//在栈空间开辟4字节

char arr[20] = {0};//在栈空间开辟20字节

上述开辟空间的方式的特点为:

1.开辟的空间大小是固定的.

2.数组在声明时必须指定数组长度,它所需要的内存在编译时分配.

但是在实际应用中我们往往不知道我们需要多大的空间才足够我们使用,太大导致空间浪费,太小不够使用.这个时候只能试试动态内存开辟了.

首先我们需要知道动态内存管理都是在堆区开辟空间的如下图

2.动态内存函数的介绍

2.1malloc和free

函数名: malloc
功  能: 动态开辟内存
用  法: void* malloc(size_t size);
返回值:

程序例:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int* p = (int*)malloc(40);
    //可以先判断p是否为NULL,空间开辟失败会开辟空指针
    if (p == NULL)
    {
        perror("malloc");//打印错误信息
        return 1;
    }
    //使用
    free(p);//使用完指针后释放开辟的内存并把该指针置为NULL
    p = NULL;

    return(0);
}

函数名: free
功  能: 释放动态开辟的内存
用  法: void free(void* ptr);
返回值: 无返回值

如果参数ptf为NULL,该函数什么都不做

程序例:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int* p = (int*)malloc(40);
    //使用
    free(p);//使用完指针后释放开辟的内存并把该指针置为NULL
    p = NULL;

    return(0);
}

2.2calloc

函数名: calloc
功  能: 动态开辟内存
用  法: void* calloc(size_t num, size_t size);
返回值: 和malloc函数一样

和malloc函数唯一的区别是calloc函数会把开辟的空间初始化为0.

程序例:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int* p = (int*)calloc(10, sizeof(int));//开辟10个整型int的空间
    //可以先判断p是否为NULL,空间开辟失败会开辟空指针
    if (p == NULL)
    {
        perror("calloc");
        return 1;
    }
    //使用
    free(p);//使用完指针后释放开辟的内存并把该指针置为NULL
    p = NULL;

    return(0);
}

2.3realloc

函数名: realloc
功  能: 调整已动态开辟的内存的大小
用  法: void* realloc(void* ptr, size_t size);
返回值: 和malloc函数一样

程序例:

#include <stdio.h>
#include <stdlib.h>

int main()

{
    int* p = (int*)calloc(10, sizeof(int));//开辟10个整型int的空间
    //可以先判断p是否为NULL,空间开辟失败会开辟空指针
    if (p == NULL)
    {
        perror("calloc");
        return 1;
    }
    //使用
    int* ptr = (int*)realloc(p, 80);
    if (ptr == NULL)
    {
        perror("realloc");
        return 1;
    }
    p = ptr;
    //扩容成功开始使用
    //不再使用,就释放
    free(p);//使用完指针后释放开辟的内存并把该指针置为NULL
    p = NULL;

    return 0;
}

3.常见动态内存错误

3.1对NULL指针的解引用操作

例如:

void test()
{
    int* p = (int*)malloc(INT_MAX);
    *p = 20;
    //解决方法,开辟动态空间后对返回的指针
    //if (p == NULL)
    //{
    //    perro("malloc");
    //    return;
    //}

    free(p);
}

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

例如:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int* p = (int*)calloc(10, sizeof(int));//开辟10个整型int的空间
    //可以先判断p是否为NULL,空间开辟失败会开辟空指针
    if (p == NULL)
    {
        perror("calloc");
        return 1;
    }
    //使用
    //越界访问了(解决方法自己对内存边界检查)

    int i = 0;
    for (i = 0; i <= 25; i++)
    {
        *(p + i) = i;
    }  

    free(p);//使用完指针后释放开辟的内存并把该指针置为NULL
    p = NULL;

   return 0;
}

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

例如:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int a = 0;
    int* p = &a;
    //......
    free(p);
    p = NULL;

    return 0;
}

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

例如:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int* p = (int*)calloc(25, sizeof(int));//开辟10个整型int的空间
    //可以先判断p是否为NULL,空间开辟失败会开辟空指针
    if (p == NULL)
    {
        perror("calloc");
        return 1;
    }
    int i = 0;
    for (i = 0; i < 10; i++)
    {
        *p = i;
        p++;
    }
    free(p);//指针p的位置移动了只释放了部分动态开辟的内存空间
    p = NULL;

    return 0;
}

3.5对同一块动态内存多次释放

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int* p = (int*)calloc(10, sizeof(int));//开辟10个整型int的空间
    //可以先判断p是否为NULL,空间开辟失败会开辟空指针
    if (p == NULL)
    {
        perror("calloc");
        return 1;
    }

    free(p);
    //......

    free(p);//err

   return 0;
}

3.6动态内存忘记释放(内存泄漏)

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int* p = (int*)calloc(10, sizeof(int));//开辟10个整型int的空间
    //可以先判断p是否为NULL,空间开辟失败会开辟空指针
    if (p == NULL)
    {
        perror("calloc");
        return 1;
    }

   return 0;
}

4.C/C++程序内存开辟

 C/C++程序内存分配的几个区域:

栈段(stack):存放函数调用相关的参数、局部变量的值,以及在任务切换的上下文信息。栈区是由操作系统分配和管理的区域。

堆段(heap): 动态内存分配的区域,也就是malloc申请的内存区,使用free()函数来释放内存,堆的申请释放工作由程序员控制,容易产生内存泄漏。

数据段(data):用来存放显式初始化的全局变量或者静态(全局)变量,常量数据。

代码段(text):就是C程序编译后的机器指令,也就是我们常见的汇编代码。

5.柔性数组

C99中结构中最后一个元素允许是未知大小的数组 ,这个数组就是柔性数组。 但 结构中的柔性数组前面必须至少一个其他成员 ,柔性数组成员允许结构中包含一个大小可变的数组,sizeof返回的这种结构大小不包括柔性数组的内存。

例如:

struct type_a
{
    int i;
    int a[0];//柔性数组成员或者int a[]也可以
};

5.1柔性数组的特点:

 5.2柔性数组的使用

#include <stdio.h>
#include <stdlib.h>

struct s1
{
    int num;
    int arr[];
};

int main()
{
    struct s1* ps = (struct s1*)malloc(sizeof(struct s1) + 40);    
    ps->num = 20;
    int i = 0;
    for (i = 0; i < 10; i++)
    {
        printf("%d ", ps->arr[i] = i);
    }
    
    //扩容
    struct s1* ptr = (struct s1*)realloc(ps, sizeof(struct s1) + 80);
    if (ptr == NULL)
    {
        perror("realloc");
        return 1;
    }
    else
    {
        ps = ptr;
    }
    //释放
    free(ps);
    ps = NULL;

    return 0;
}

5.3柔性数组的优势

代码1

代码2

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值