【C语言】动态内存管理

目录

前言:

为什么需要动态内存管理?

静态内存分配:在编译时确定大小

自动内存分配:在栈空间分配

这些传统分配方式的局限性:

一·动态内存管理核心函数

1. malloc:基础内存分配

使用分配的内存 

释放内存

2.calloc():带初始化的分配

3. realloc:内存调整

4.free:内存释放

注意事项: 

双重释放:

释放未分配的内存:

使用已经释放的内存:

空指针:

 总结:


前言:

在上一章节讲解了自定义类型:联合体和枚举的使用,和注意事项。

在本章节为大家带来动态内存管理的文章,希望有所帮助。

在C语言中,动态内存管理是一项核心技能,它允许程序员在程序运行时根据需要分配和释放内存。与静态内存分配相比,动态内存管理提供了更大的灵活性,能够应对不确定或变化的数据大小。然而,动态内存管理也伴随着一些风险,如内存泄漏、野指针和悬挂指针等问题。本文将从动态内存管理的基本概念、常用函数、常见错误及其解决方法等方面进行详细讲解。

为什么需要动态内存管理?

在C语言编程中,我们通常使用两种内存分配方式:

  1. 静态内存分配:在编译时确定大小

    • 全局变量

    • 静态局部变量

    • 固定大小的数组

  2. 自动内存分配:在栈空间分配

    • 局部变量

    • 函数参数

这些传统分配方式的局限性

  • 内存大小固定,无法动态调整

  • 栈空间有限(通常1-8MB)

  • 无法在运行时决定内存需求

当需要处理以下场景时,动态内存管理就成为必需:

  • 文件读取(文件大小未知)

  • 网络数据接收

  • 复杂数据结构(链表、树等)

  • 大内存需求(如图像处理)

区别:

  1. 静态内存与动态内存的区别
    静态内存是在编译时分配的,其大小固定且不可改变。例如,局部变量和全局变量通常存储在栈中,数组的大小在声明时必须指定。而动态内存则是在程序运行时通过函数动态分配的,可以根据实际需求调整内存大小。

  2. 堆与栈的区别
    堆是用于动态内存分配的区域,由程序员手动管理;栈是用于存储局部变量和函数调用信息的区域,由系统自动管理。堆上的内存分配和释放需要程序员手动控制,而栈上的内存则在函数结束时自动释放。

一·动态内存管理核心函数

1. malloc:基础内存分配

void* malloc(size_t size);
  • 分配指定字节的未初始化内存

  • 这个函数开辟一个连续可用的空间,并返回指向该空间的指针。

  • 返回void*类型指针

  • 示例: 分配一个整数的内存

  • int *arr = (int*)malloc(10 * sizeof(int));  // 分配40字节(假设int为4字节)
    if (arr == NULL) 
    {
       printf("Failed to allocate memory.\n");
        // 处理分配失败
    }

使用分配的内存 

一旦你检查过返回的指针不是 NULL,你就可以像使用普通指针那样使用它:

*p = 5; // Assigns 5 to the allocated memory

释放内存

当你分配了一块内存并且完成了对它的使用,你应该调用 free() 来释放这块内存,这可以防止内存泄漏:

free(p);

 

请注意,一旦你释放了一块内存,你就不应再使用指向它的指针,除非你再次给它分配了新的内存。使用已经释放的内存通常会导致程序崩溃或其他未定义的行为。 

2.calloc():带初始化的分配

calloc() 函数在 C 语言中用于动态内存分配,其功能和 malloc() 类似,但带有两个重要不同之处:首先,calloc() 除了分配内存外,还会将内存初始化为零;其次,calloc() 接收两个参数,一个表示元素的数量,另一个表示单个元素的大小。

 void *calloc(size_t num, size_t size)

它会分配 num * size 字节的内存,并返回指向新分配内存的指针。如果分配失败,它将返回 NULL。

以下是 calloc() 的使用示例:

// 分配足够的内存来保存 10 个整数,并将内存初始化为 0

int *arr = (int*)calloc(10, sizeof(int));

// 检查内存分配是否成功

if (arr == NULL) {

printf("Failed to allocate memory.\n");

// 处理错误

}

和 malloc() 一样,你需要检查 calloc() 是否成功分配了内存,即检查返回的指针是否为 NULL。

你可以像使用普通数组一样使用动态分配的内存:

for (int i = 0; i < 10; i++) 
{

arr[i] = i;

}

和 malloc() 一样,当你完成对动态分配内存的使用后,必须使用 free() 函数来释放内存,以防止内存泄漏:

free(arr);

请注意,一旦内存被释放,你不应再使用指向该内存的指针,除非你再次给它分配了新的内存。使用已经释放的内存可能会导致程序崩溃或其他未定义的行为。

 

3. realloc:内存调整

realloc() 函数在 C 语言中用于重新分配之前已分配的内存区域。当你需要改变已分配的内存大小时,可以使用 realloc() 函数。

void* realloc(void* ptr, size_t new_size);

ptr 是指向之前已分配的内存的指针,size 是新的内存大小。realloc() 会尝试在原地调整内存大小,如果无法做到,它将分配一块新的内存,将旧数据复制过去,然后释放旧内存。 

以下是 realloc() 的使用示例:

// 开始时分配足够的内存来保存 10 个整数

int *arr = (int*)malloc(10 * sizeof(int));

// ……在这里使用内存……

// 现在我们需要更多的空间,所以重新分配内存以保存 20 个整数

arr = (int*)realloc(arr, 20 * sizeof(int));

// 检查内存重新分配是否成功

if (arr == NULL) {

printf("Failed to allocate memory.\n");

// 处理错误

}

和 malloc()、calloc() 一样,你需要检查 realloc() 是否成功分配了内存,即检查返回的指针是否为 NULL。

当你完成对动态分配内存的使用后,必须使用 free() 函数来释放内存,以防止内存泄漏:

free(arr);

请注意,一旦内存被释放,你不应再使用指向该内存的指针,除非你再次给它分配了新的内存。使用已经释放的内存可能会导致程序崩溃或其他未定义的行为。

 

 

 

4.free:内存释放

free() 函数在 C 语言中用于释放之前通过 malloc(), calloc(), 或 realloc() 函数分配的内存。

动态分配的内存不会像自动变量那样在生命周期结束时自动释放。如果你不使用 free(),那么在程序运行时分配的内存将会一直存在,可能导致内存泄漏问题。 

void free(void* ptr);
  • ptr 是指向要释放的内存的指针。一旦内存被 free() 释放,该内存区域就可以被操作系统重新分配使用了。

  • 避免内存泄漏

  • 注意:释放后指针应置NULL

// 分配内存
int *p = (int*)malloc(sizeof(int));
*p = 5;
// 使用完内存后,释放它
free(p);

注意事项: 

双重释放

如果你尝试释放同一块内存两次,会导致未定义行为,通常会导致程序崩溃。

例如,下面的代码就是错误的:

int *p = (int*)malloc(sizeof(int));
free(p);
free(p); // 错误:p 已被释放
释放未分配的内存

只有通过 malloc(), calloc(), realloc() 函数分配的内存才能被 free() 释放。你不能释放栈变量或全局变量的内存。

使用已经释放的内存

一旦你释放了一块内存,就不能再使用它,除非你再次给它分配了新的内存。例如,下面的代码就是错误的:

int *p = (int*)malloc(sizeof(int));

free(p);

*p = 5; // 错误:p 已被释放
空指针

对 NULL 指针调用 free() 是安全的,不会有任何效果。这是一种用来处理可能未分配内存的指针的好方法。

int *p = NULL;

free(p); // 安全:没有任何效果

 总结:

本章节介绍了为什么需要动态内存管理?和动态内存管理的函数.

关键要点

  1. 为每个malloc编写对应的free

  2. 使用静态分析工具检查代码

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值