C 教程

C 教程 C 语言简介 C 下载安装和环境配置 C 快速入门 C 语法 C 输出 C 注释 C 变量 C 数据类型 C 常量 C 运算符 C 布尔值 C If...Else 语句 C Switch 语句 C While 循环 C For 循环 C Break/Continue 语句 C 数组 C 字符串 C 用户输入 C 内存地址 C 指针

C 函数

C 函数 C 函数参数 C 作用域 C 函数声明 C 递归 C 数学函数

C 文件

C 创建文件 C 写入文件 C 读取文件

C 结构体

C 结构体 C 嵌套结构体 C 结构体和指针 C 联合体 C typedef

C 枚举

C 枚举

C 内存

C 内存管理

C 错误

C 错误 C 调试 C 空值 C 错误处理 C 输入验证

C 更多

C 日期 C 随机数 C 宏 C 代码组织 C 存储类 C 位运算符 C 固定宽度整数

C 项目

C 项目

C 参考资料

C 参考资料 C 关键词 C <stdio.h> C <stdlib.h> C <string.h> C <math.h> C <ctype.h> C <time.h>

C 标准库参考

C 标准库 - 参考手册 C 标准库 - <assert.h> C 标准库 - <ctype.h> C 标准库 - <errno.h> C 标准库 - <float.h> C 标准库 - <limits.h> C 标准库 - <locale.h> C 标准库 - <math.h> C 标准库 - <setjmp.h> C 标准库 - <signal.h> C 标准库 - <stdarg.h> C 标准库 - <stddef.h> C 标准库 - <stdio.h> C 标准库 - <stdlib.h> C 标准库 - <string.h> C 标准库 - <time.h>

C 实例

C 语言实例 C 经典100例 C 编译器 C 测验


C 内存管理示例

实际内存管理示例

为了演示动态内存的实际应用,我们编写了一个可以生成任意长度列表的程序。

C 语言中的普通数组长度固定且无法更改,但使用动态内存,我们可以创建任意长度的列表:

示例

struct list {
  int *data; // 指向存储列表项的内存位置。
  int numItems; // 指示列表中当前有多少个项目
  int size; // 表示分配的内存可以容纳多少个项目
};

void addToList(struct list *myList, int item);

int main() {
  struct list myList;
  int amount;
  int i, j;

  // 创建一个清单,并预留足够的空间容纳 10 项内容。
  myList.numItems = 0;
  myList.size = 10;
  myList.data = malloc(myList.size * sizeof(int));

  // 确定内存分配是否成功
  if (myList.data == NULL) {
    printf("Memory allocation failed");
    return 1; // 程序退出并返回错误代码
  }

  // 向列表中添加任意数量的项目,数量由 amount 变量指定。
  amount = 44;
  for (i = 0; i < amount; i++) {
    addToList(&myList, i + 1);
  }

  // 显示列表内容
  for (j = 0; j < myList.numItems; j++) {
    printf("%d ", myList.data[j]);
  }

  // 不再需要内存时,请释放它。
  free(myList.data);
  myList.data = NULL;

  return 0;
}

// 此函数向列表中添加项目
void addToList(struct list *myList, int item) {

  // 如果列表已满,则调整内存大小以容纳另外 10 个项目。
  if (myList->numItems == myList->size) {
    int newSize = myList->size + 10;

    // 使用临时指针,这样即使失败也不会丢失原始指针。
    int *tmp = realloc(myList->data, newSize * sizeof(int));
    if (tmp == NULL) {
      printf("Memory resize failed ");
      return; // 列表保持不变。
    }

    // 仅在成功重新分配后更新字段
    myList->data = tmp;
    myList->size = newSize;
  }

  // 将该项添加到列表末尾
  myList->data[myList->numItems] = item;
  myList->numItems++;
}
亲自试一试 »

指向结构的指针: 此示例包含一个指向结构体 `myList` 的指针。由于我们使用的是指向结构的指针而不是结构体本身,因此我们使用箭头语法(`->`)来访问结构体的成员。

示例详解

本示例包含三个部分:

  • 包含列表数据的结构体 myList
  • 包含程序的 main() 函数。
  • 向列表添加元素的 addToList() 函数。

myList 结构

myList 结构包含列表的所有信息,包括其内容。它有三个成员:

  • data - 指向包含列表内容的动态内存的指针
  • numItems - 指示列表中的项数
  • size - 表示分配的内存可以容纳多少个项目

我们使用结构体,以便轻松地将所有这些信息传递给函数。

ma​​in() 函数

main() 函数首先初始化一个可容纳 10 个元素的列表:

// 创建一个清单,并预留足够的空间容纳 10 项内容。
myList.numItems = 0;
myList.size = 10;
myList.data = malloc(myList.size * sizeof(int));

myList.numItems 设置为 0,因为列表初始为空。

myList.size 用于跟踪已分配的内存大小。我们将其设置为 10,因为我们将为 10 个元素分配足够的内存。

然后我们分配内存,并将指向该内存的指针存储在 myList.data 中。

接下来我们添加错误检查,以确定内存分配是否成功。

// 确定内存分配是否成功
if (myList.data == NULL) {
  printf("Memory allocation failed");
  return 1; // 程序退出并返回错误代码
}

如果一切正常,循环会使用 addToList() 函数向列表中添加 44 个项目:

// 向列表中添加任意数量的项目,数量由 amount 变量指定。
amount = 44;
for (i = 0; i < amount; i++) {
  addToList(&myList, i + 1);
}

在上面的代码中,&myList 是指向列表的指针,i + 1 是我们想要添加到列表中的数字。我们选择 i + 1 是为了让列表从 1 而不是 0 开始。您可以选择任何数字添加到列表中。

所有项目添加到列表后,下一个循环会打印列表的内容。

// 显示列表内容
for (j = 0; j < myList.numItems; j++) {
  printf("%d ", myList.data[j]);
}

打印完列表后,我们会释放内存以防止内存泄漏。

// 不再需要内存时,请释放内存。
free(myList.data);
myList.data = NULL;

addToList() 函数

addToList() 函数用于向列表中添加项目。它接受两个参数:

void addToList(struct list *myList, int item)
  1. 指向列表的指针。
  2. 要添加到列表中的值。

该函数首先检查列表是否已满,方法是将列表中的元素数量与列表的大小(容量)进行比较。 如果列表已满,它会尝试扩展内存以容纳另外 10 个元素。 我们使用一个临时指针和realloc,这样即使调整大小失败,我们也不会丢失原始内存块。

我们仅在成功调整大小后更新data和size。

// 如果列表已满,则调整内存大小以容纳另外 10 个项目。
if (myList->numItems == myList->size) {
  int newSize = myList->size + 10;

  // 使用临时指针,这样即使失败也不会丢失原始指针。
  int *tmp = realloc(myList->data, newSize * sizeof(int));
  if (tmp == NULL) {
    printf("Memory resize failed
");
    return; // 列表保持不变。
  }

  // 仅在成功重新分配后更新字段
  myList->data = tmp;
  myList->size = newSize;
}

最后,该函数将项目添加到列表末尾。索引 myList->numItems 始终位于列表末尾,因为每次添加新项目时,该索引都会递增 1。


// 将该项添加到列表末尾
myList->data[myList->numItems] = item;
myList->numItems++;

为什么我们每次只预留 10 个项目?

优化需要在内存和性能之间取得平衡。即使我们分配了一些未使用的内存,频繁地重新分配内存也会降低效率。内存分配过多和分配过于频繁之间存在一个平衡点。

在这个例子中,我们选择了 10 这个数字,但这取决于您预期的数据量以及数据变化的频率。例如,如果我们事先知道会有 44 个项目,那么我们可以一次性分配 44 个项目的内存。


完整的标准库参考

如需完整的内存管理函数和标准库中其他函数的参考,请访问C<stdlib.h>库参考。