C 语言动态内存分配

2025 年 7 月 3 日 | 10 分钟阅读

在 C 编程中,动态内存分配 (DMA) 是一个重要特性,它允许程序在运行时分配内存。与静态内存分配(内存是在编译时保留的)相反,动态分配允许程序员在运行时控制内存的请求、调整大小和释放。

什么是 C 中的内存分配?

C 编程 中,内存分配是指在程序执行期间,为存储数据或程序指令而保留计算机一部分空间的过程。它是在运行时动态管理数据的非常重要的特性,特别是当数据结构的大小未知时。 C 中有两种主要的内存分配方式:

  • 静态内存分配: 在静态内存分配中,内存大小在编译时确定(例如,数组,如 int arr[100])。
  • 动态内存分配: 在这种内存分配中,内存大小通过 <stdlib.h> 库中的函数在程序运行时确定。

在程序运行时分配和释放内存的过程称为动态内存分配。动态分配通过 <stdlib.h> 中声明的特殊库函数提供了一种在运行时请求和释放内存的方法。另一方面,静态内存分配在编译时确定内存大小。

C 语言中的动态内存分配可以通过 stdlib.h 头文件中的四个函数来实现。

  • malloc()
  • calloc()
  • realloc()
  • free()

现在,让我们快速浏览一下用于动态内存分配的方法。

malloc()它用于分配请求内存的单个块。
calloc()它用于分配请求内存的多个块。
realloc()它用于重新分配 malloc() 或 calloc() 函数占用的内存。
free()它用于释放动态分配的内存。

在这里,我们将逐一讨论这些内存分配。

C 中的 malloc() 函数

在 C 编程中,malloc() 函数用于分配请求内存的单个块。它在执行时不会初始化内存,因此最初它具有垃圾值。如果内存不足,它会返回 NULL。

语法

它具有以下语法:

C Malloc 示例

让我们看一个示例来说明 C 中的 malloc() 函数。

示例

编译并运行

输出

Enter elements of an array: 3
Enter elements of an array: 10
10
10
Sum=30

说明

在此示例中,我们演示了使用 malloc() 函数进行动态内存分配。首先,我们在运行时为整数数组分配内存。之后,它提示用户输入数组元素的数量,使用指针算术计算它们的总和,然后使用 free() 释放已分配的内存。

Malloc 的关键特性

malloc 在 C 中的几个关键特性如下:

  • 它重新分配未初始化的内存。
  • 它返回一个通用指针 void*。
  • 它在 C 中需要类型转换(例如,(int *)malloc(...))。

C 中的 calloc() 函数

在 C 编程中,calloc() 函数用于分配请求内存的多个块。它最初将所有字节初始化为零。如果内存不足,它会返回 NULL。

语法

它具有以下语法:

C calloc() 函数示例

让我们看一个 C 中 calloc() 函数的示例。

示例

编译并运行

输出

Enter elements of an array: 3
Enter elements of an array: 10
10
10
Sum=30

说明

在此示例中,我们演示了使用 calloc() 函数进行动态内存分配,该函数将分配的内存初始化为零。之后,我们从用户那里获取元素数量,将它们存储在动态分配的数组中,使用指针算术计算所有元素的总和,然后使用 free() 函数释放内存。

calloc() 函数的关键特性

calloc() 函数在 C 中的几个关键特性如下:

  • 它为 num 个元素数组保留内存,每个元素的大小为 size。
  • 它将所有字节设置为零。

C 中的 realloc() 函数

在 C 编程中,realloc() 函数用于重新分配 malloc() 或 calloc() 函数占用的内存。换句话说,它会更改内存大小。

语法

它具有以下语法:

C realloc() 函数示例

让我们看一个 C 中 realloc() 函数的示例。

示例

编译并运行

输出

Original array elements:
1 2 3 4 5 
Resized array elements:
1 2 3 4 5 60 70 80 90 100 

说明

在此示例中,我们演示了使用 malloc() 和 realloc() 函数进行动态内存分配。首先,我们为 5 个整数分配内存,赋值并打印它们。之后,它将内存大小调整为容纳 10 个整数,为添加的元素赋值,并打印更新后的数组。

主要特点

realloc() 函数在 C 中的几个关键特性如下:

  • 它调整 ptr 指示的内存块的大小。
  • 它(在必要时)将旧数据更新到新块。
  • 失败时返回 NULL。

C 中的 free() 函数

在 C 编程中,必须通过调用 free() 函数来释放 malloc() 或 calloc() 函数占用的内存。否则,它将一直占用内存直到程序退出。

语法

它具有以下语法:

C free() 函数示例

让我们看一个示例来说明 C 中的 free() 函数。

示例

编译并运行

输出

Enter the number of elements: 5
Enter five integers:
Element 1: 10
Element 2: 20
Element 3: 30
Element 4: 40
Element 5: 50
You entered:
10 20 30 40 50 
Memory successfully freed.

说明

在此示例中,我们使用 malloc() 函数存储用户输入的 n 个整数。之后,它打印这些整数,并使用 free() 函数释放分配的内存。

为什么我们需要 C 中的动态内存分配?

在 C 编程中,内存需求并非总能在实际编程中预测。考虑输入仅在运行时才能知道的情况,例如用户提供的值或外部来源的数据。程序涉及动态增长和收缩数据结构,如链表、堆栈、队列或树。应优化内存使用,特别是在资源受限的环境中。

在这种情况下,动态内存分配是必要的,因为它提供了:

  • 运行时灵活性: 内存可以在需要时分配,取决于当前情况或当时的交互式用户,为程序提供了在运行时调整自身的机会。
  • 内存的有效利用: 动态分配给出的内存量恰好是所需的,而不是通过过度分配或由于分配不足而导致的溢出而浪费空间。
  • 可扩展的数据结构: 链表、图、树、动态数组等数据结构高度依赖于动态内存,因为它们无法提前确定大小,甚至可能需要在运行时调整大小。

C 中动态内存分配的挑战

C 中动态内存分配存在几个挑战。其中一些如下:

1) 内存泄漏

动态分配最常见的问题之一是使用后未能释放内存。这会导致内存泄漏,即内存未释放但不再可用。

2) 悬空指针

当内存被释放时,指针会保留已释放块的地址。解引用这样的 指针 或通过它进行数据赋值会导致未定义行为——这可能会导致程序崩溃,或者可能损坏数据。

3) 分配失败

当系统内存不足时(例如,当请求一个非常大的块时),malloc() 或 calloc() 等函数将返回 NULL。

4) sizeof 的不当使用

一个不太明显且更常见的错误是编写 sizeof(ptr) 而不是 sizeof(*ptr) 或适当的数据类型,这可能导致分配的内存量不正确。

5) 碎片化

反复分配和释放小内存块可能导致碎片化,即可用内存总量足够,但不是连续的块。

6) 过度使用动态内存

并非所有问题都需要动态分配。不一定需要通过过度使用来使其复杂化和冒险。

静态内存分配与动态内存分配的区别

在学习上述函数之前,让我们先了解静态内存分配和动态内存分配之间的区别。

静态内存分配动态内存分配
内存是在编译时分配的。内存是在运行时分配的。
程序执行期间无法增加内存。程序执行期间可以增加内存。
它用于数组。它用于链表。
它在程序的整个生命周期内都存在。它一直存在,直到使用 free() 显式释放。
在堆栈或全局内存中分配。在堆上分配。
由编译器自动管理。程序员负责分配和去分配。
可以在声明时初始化。默认不初始化(malloc() 给出垃圾值;calloc() 初始化为零)。
由于内存已预先分配,因此稍快。由于运行时分配开销,速度稍慢。
运行时出现内存错误的几率较小。它需要仔细处理以避免内存泄漏、悬空指针和分配失败等问题。

结论

总而言之,动态内存分配使程序更加灵活和高效,因为内存分配发生在运行时。malloc()、calloc() 和 realloc() 等动态函数有助于根据不断变化的需求管理内存,而 free() 用于释放内存以防止泄漏。

它在处理动态数据结构(如数组和链表)时特别有用。但是,如果使用不当,它们可能导致内存泄漏或崩溃,因此必须谨慎使用。总的来说,动态分配能够实现高效且可扩展的 C 程序设计。

C 中的动态内存分配常见问题解答

1) C 中的动态内存分配是什么?它与静态分配有何不同?

在 C 编程中,动态内存分配是在程序运行时分配内存的过程,而不是在编译时分配。它与堆栈内存分配不同,堆栈内存分配需要预先确定内存大小。当数据大小未知或数据结构需要在运行时扩展或收缩时,它非常有用。

2) 为什么动态内存分配在 C 程序中很重要?

在 C 编程中,动态内存分配使程序能够以灵活且可扩展的方式分配内存。例如,在创建动态数组、链表、树或图时,数据的大小和形状直到运行时才知道。

3) 是否必须释放动态分配的内存?如果不这样做会怎样?

是的,必须使用 free() 函数释放动态分配的内存。未能释放未使用的内存可能导致程序出现内存泄漏,即内存无法访问但仍被程序占用。

4) realloc() 如何工作?何时应使用它?

在 C 编程中,realloc() 函数可以正确调整先前分配的内存块的大小并保留其内容。当我们最初分配内存然后发现需要更少或更多的空间时,它会特别有用。

5) 动态内存分配的常见问题有哪些?

在 C 编程中,动态内存分配的灵活性也带来了一些潜在的陷阱。内存泄漏是最严重的问题之一,已分配的内存未被正确释放,导致内存消耗缓慢。悬空指针是另一个可能导致崩溃或不可预测行为的问题。


下一主题C 字符串