当前位置: 首页 > backend >正文

C 语 言 - - - 动 态 内 存 分 配

C 语 言 - - - 动 态 内 存 分 配

  • 动 态 内 存 分 配
  • malloc
  • free
    • p = NULL 的 意 义
  • calloc
  • realloc
  • 常 见 的 动 态 内 存 错 误
  • 柔 性 数 组
  • 总结

💻作 者 简 介:曾 与 你 一 样 迷 茫,现 以 经 验 助 你 入 门 C 语 言
💡个 人 主 页:@ 笑 口 常 开 xpr 的 个 人 主 页
📚系 列 专 栏:C 启 新 程
✨代 码 趣 语:不 要 担 心 程 序 异 常,如 果 它 总 是 正 确 的 话 你 早 就 失 业 了。
💪代 码 千 行,始 于 坚 持,每 日 敲 码,进 阶 编 程 之 路。
📦gitee 链 接:gitee

在这里插入图片描述

         在 编 程 的 世 界 里,每 一 行 代 码 都 可 能 隐 藏 着 无 限 的 可 能 性 。你 是 否 想 过,一 个 小 小 的 程 序 究 竟 能 改 变 什 么?它 可 以 是 解 决 复 杂 问 题 的 工 具 ,也 可 以 是 实 现 梦 想 的 桥 梁。今 天,就 让 我 们 一 起 走 进 C 语 言 动 态 内 存 分 配 的 世 界,探 索 它 的 无 限 潜 力。

动 态 内 存 分 配

开 辟 空 间

目 前 已 经 掌 握 下 面 两 种 开 辟 空 间 的 方 式

int a = 20;//在栈空间上开辟四个字节
char arr[10] = { 0 };//在栈空间上开辟10个字节的连续空间

特 点

空 间 开 辟 大 小 是 固 定 的。

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

定 义

         在 C 语 言 里,动 态 内 存 分 配 指 的 是 在 程 序 运 行 时,依 据 实 际 需 求 对 内 存 进 行 分 配 与 释 放 的 操 作。

malloc

在这里插入图片描述

定 义

         在 C 语 言 里,malloc 是 一 个 标 准 库 函 数,其 主 要 功 能 是 在 程 序 运 行 期 间 动 态 地 分 配 内 存。malloc 函 数 定 义 于 <stdlib.h> 头 文 件 中。

函 数 原 型

void* malloc(size_t size);

函 数 参 数

size:代 表 要 分 配 的 内 存 块 的 字 节 数。size_t 是 一 种 无 符 号 整 数 类 型,专 门 用 于 表 示 对 象 的 大 小。

返 回 值:若 内 存 分 配 成 功,malloc 会 返 回 一 个 指 向 所 分 配 内 存 块 起 始 地 址 的 void* 类 型 指 针。由 于 void* 类 型 指 针 可 以 转 换 为 任 意 类 型 的 指 针,所 以 它 能 用 来 指 向 不 同 类 型 的 数 据,因 此 在 使 用 malloc 时 要 进 行 强 制 类 型 转 换。要 是 内 存 分 配 失 败( 比 如 系 统 没 有 足 够 的 可 用 内 存 ),malloc 就 会 返 回 NULL。

如 果 参 数 size 为 0,malloc 的 行 为 是 标 准 是 未 定 义 的,取 决 于 编 译 器。

free

在这里插入图片描述

定 义

         free 函 数 是 用 于 释 放 动 态 分 配 内 存 的 函 数,一 般 将 malloc 等 函 数 和 free 结 合 起 来 使 用。当 使 用 malloc 等 函 数 在 堆 上 动 态 分 配 内 存 后,这 些 内 存 块 在 程 序 运 行 期 间 会 一 直 被 占 用,直 到 被 释 放。free 函 数 的 作 用 就 是 将 之 前 动 态 分 配 的 内 存 归 还 给 系 统,使 得 这 些 内 存 可 以 被 后 续 的 内 存 分 配 请 求 再 次 使 用。

函 数 原 型

void free(void* ptr);

函 数 参 数

参 数:ptr 是 一 个 void* 类 型 的 指 针,它 指 向 之 前 通 过 malloc 等 函 数 分 配 的 内 存 块 的 起 始 地 址。

返 回 值:free 函 数 没 有 返 回 值,其 返 回 类 型 为 void。

下 面 展 示代码示例

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
int main()
{//申请40个字节,用来存放10个整型int* p = (int*)malloc(10 * sizeof(int));if (p == NULL){printf("%s\n", strerror(errno));return 1;}//存放1~10int i = 0;for (i = 0;i < 10;i++){*(p + i) = i + 1;}for (i = 0;i < 10;i++){printf("%d ", *(p + i));}printf("\n");//free释放申请的内存free(p);p = NULL;return 0;
}

在这里插入图片描述

p = NULL 的 意 义

p = NULL 的 意 义 是 避 免 野 指 针 风 险。

野 指 针 的 危 害

定 义:野 指 针 是 指 指 向 无 效 内 存 地 址 或 已 释 放 内 存 的 指 针。

风 险:访 问 野 指 针 会 导 致 程 序 崩 溃(段 错 误)或 数 据 损 坏,属 于 未 定 义 行 为。

free ( p ) 后 为 何 需 要 p = NULL?

free(p);  //释放内存,但p仍指向已释放的地址(成为野指针)
p = NULL; //将p置为NULL,明确标识指针不再指向有效内存

释 放 内 存 的 本 质:free( p ) 仅 释 放 p 指 向 的 内 存 空 间,但 不 会 自 动 修 改 指 针 p 本 身 的 值。此 时 p 仍 然 保 存 着 已 释 放 内 存 的 地 址,成 为 野 指 针。

置 空 的 作 用:将 p 设 为 NULL 后,后 续 若 误 操 作 访 问 p(如 *p = 10;),会 访 问 NULL 地 址( 空 指 针 ),触 发 程 序 崩 溃( 而 非 访 问 已 释 放 的 内 存 ),从 而 在 开 发 阶 段 更 快 发 现 错 误。

建 议

释 放 内 存 后 立 即 置 空 指 针

free(p);  //释放内存
p = NULL; //置空,防止野指针

初 始 化 指 针:声 明 指 针 时 默 认 置 空,避 免 未 初 始 化 的 野 指 针。

int* p = NULL;     	   //初始化指针为NULL
p = (int*)malloc(100); //分配内存

检 查 指 针 有 效 性:在 使 用 指 针 前,确 保 其 不 为 NULL

if (p != NULL) 
{// 使用指针
}

总 结

操 作作 用
free( p )释放p指向的内存空间,但p成为野指针(指向无效地址)
p = NULL主动消除野指针,明确指针不再指向任何有效内存,避免后续误操作
free( p )(置空后)安全操作,对 NULL 调用 free 无副作用

calloc

在这里插入图片描述

定 义

         calloc 是 C 语 言 标 准 库 中 的 一 个 函 数,用 于 在 堆 上 动 态 分 配 内 存。calloc 函 数 定 义 于 <stdlib.h> 头 文 件 中。

函 数 原 型

void* calloc(size_t num, size_t size);

函 数 参 数

num - - - 需 要 分 配 的 元 素 数 量
size - - - 每 个 元 素 的 大 小( 以 字 节 为 单 位 )

返 回 值

         若 内 存 分 配 成 功,calloc 会 返 回 一 个 指 向 分 配 内 存 块 起 始 位 置 的 指 针。返 回 的 指 针 类 型 为 void*,这 意 味 着 你 可 以 将 其 转 换 为 任 意 类 型 的 指 针。

若 内 存 分 配 失 败( 例 如 系 统 没 有 足 够 的 内 存),calloc 会 返 回 NULL。

功 能

         calloc 函 数 的 主 要 功 能 是 为 一 个 包 含 num 个 元 素,每 个 元 素 大 小 为 size 字 节 的 数 组 分 配 内 存 空 间,并 且 会 将 分 配 到 的 内 存 空 间 的 每 一 个 字 节 都 初 始 化 为 0。

与 malloc 的 区 别

初 始 化 方 面:malloc 只 是 单 纯 地 分 配 指 定 大 小 的 内 存 空 间,不 会 对 这 块 内 存 进 行 初 始 化,因 此 这 块 内 存 中 的 值 是 未 定 义 的。而 calloc 会 把 分 配 到 的 内 存 空 间 的 每 个 字 节 都 初 始 化 为 0。
参 数 方 面:malloc 只 接 受 一 个 参 数,即 需 要 分 配 的 内 存 字 节 数;calloc 接 受 两 个 参 数, 分 别 是 元 素 数 量 和 每 个 元 素 的 大 小。

下 面 展 示代 码 示 例

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
int main()
{int* p = calloc(10, sizeof(int));if (NULL == p){printf("%s\n", strerror(errno));return 1;}int i = 0;for (i = 0;i < 10;i++){*(p + i) = i + 1;}for (i = 0;i < 10;i++){printf("%d ", *(p + i));}printf("\n");free(p);p = NULL;return 0;
}

在这里插入图片描述

realloc

在这里插入图片描述
在这里插入图片描述

定 义

         realloc 是 C 语 言 标 准 库 中 的 一 个 函 数,用 于 重 新 分 配 动 态 内 存。calloc 函 数 定 义 于 <stdlib.h> 头 文 件 中。

函 数 原 型

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

函 数 参 数

ptr - - - 指 向 要 重 新 分 配 内 存 的 指 针,该 指 针 必 须 是 先 前 通 过 malloc、calloc 或 realloc 函 数 分 配 得 到 的。

size - - - 指 定 重 新 分 配 后 的 内 存 块 大 小,以 字 节 为 单 位。

返 回 值

         成 功 时,返 回 指 向 重 新 分 配 后 的 内 存 块 的 指 针。这 个 指 针 可 能 与 传 入 的 ptr 相 同,也 可 能 是 一 个 新 的 地 址。

         失 败 时,返 回 NULL,并 且 不 会 修 改 原 来 的 内 存 块。此 时,需 要 检 查 errno 变 量 来 确 定 错 误 原 因。

注 意 事 项

不 能 对 非 malloc、calloc 或 realloc 分 配 的 指 针 使 用 realloc。

         当 使 用 realloc 扩 大 内 存 块 时,可 能 会 导 致 内 存 碎 片。因 为 系 统 可 能 无 法 在 原 内 存 块 的 相 邻 位 置 找 到 足 够 的 空 闲 空 间 来 进 行 扩 展,这 时 realloc 会 分 配 一 个 新 的 内 存 块,并 将 原 内 存 块 的 内 容 复 制 到 新 内 存 块 中,然 后 释 放 原 内 存 块。

         当 使 用 realloc 缩 小 内 存 块 时,超 出 新 大 小 的 部 分 内 存 会 被 释 放,但 原 内 存 块 中 的 数 据 不 会 被 修 改。

         在 使 用 realloc 后,一 定 要 更 新 指 向 该 内 存 块 的 指 针,因 为 返 回 的 指 针 可 能 与 原 来 的 指 针 不 同。

下 面 展 示代 码 示 例

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
int main()
{int* p = (int*)malloc(5 * sizeof(int));if (NULL == p){printf("%s\n", strerror(errno));return 1;}int i = 0;//使用for (i = 0;i < 5;i++){*(p + i) = i;}//不够了,使用realloc添加空间int* ptr = (int*)realloc(p, 15 * sizeof(int));if (NULL == ptr){//printf("%s\n", strerror(errno));perror("ptr");return 1;}else{p = ptr;}for (i = 5;i < 15;i++){*(p + i) = i;}for (i = 0;i < 15;i++){printf("%d ", *(p + i));}printf("\n");//释放空间free(p);p = NULL;return 0;
}

在这里插入图片描述

注 意

         下 面 两 句 代 码,首 先 用 malloc 函 数 给 指 针 p 分 配 了 5 * sizeof(int),即 20 个 大 小 的 内 存 空 间,接 着 运 用 realloc 函 数 把 p 所 指 向 的 内 存 空 间 重 新 分 配 为 15 * sizeof(int) ,即 现 在 p 的 空 间 总 大 小 是 60 个 字 节,相 当 于 在 原 来 的 基 础 上 添 加 了 40 个 字 节,并 且 让 指 针 ptr 指 向 重 新 分 配 后 的 内 存 空 间。

int* p = (int*)malloc(5 * sizeof(int));
int* ptr = (int*)realloc(p, 15 * sizeof(int));

realloc 的 第 一 个 参 数 如 果 是 NULL,则 相 当 于 malloc。

常 见 的 动 态 内 存 错 误

对 NULL 指 针 的 解 引 用 操 作

下 面 展 示代 码 示 例

#include <stdio.h>
#include <stdlib.h>
void test()
{int* p = (int*)malloc(INT_MAX / 4);*p = 20;//如果p的值是NULL,就会有问题free(p);
}
int main()
{test();return 0;
}

         上 面 的 代 码 中,如 果 malloc 没 有 成 功 开 辟 空 间 则 会 返 回 一 个 空 指 针,对 空 指 针 解 引 用 代 码 会 报 错,最 好 使 用 if 语 句。

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

下 面 展 示代 码 示 例

#include <stdio.h>
#include <stdlib.h>
int main()
{int* p = (int*)malloc(100);if (NULL == p){return 1;}int i = 0;for (i = 0;i < 100;i++){*(p + i) = i + 1;}for (i = 0;i < 100;i++){printf("%d ", *(p + i));}free(p);p = NULL;return 0;
}

         上 面 的 代 码 中,只 开 辟 了 100 个 字 节 的 空 间 而 实 际 却 使 用 了 400 个 字 节,产 生 了 越 界 访 问,代 码 会 报 错。

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

下 面 展 示代 码 示 例

#include <stdio.h>
#include <stdlib.h>
int main()
{int a = 10;int* p = &a;free(p);p = NULL;return 0;
}

如 果 没 有 动 态 内 存 分 配 函 数,则 不 能 使 用 free 函 数 释 放 空 间。

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

下 面 展 示代 码 示 例

#include <stdio.h>
#include <stdlib.h>
int main()
{int* p = (int*)malloc(100);if (p == NULL){return 1;}free(p);free(p);return 0;
}

         对 同 一 块 动 态 内 存 多 次 释 放,这 个 操 作 是 不 允 许 的。如 果 想 要 多 次 释 放 可 以 参 考 下 面 的 代 码。

free(p); //第一次释放有效内存
p = NULL;
free(p); //第二次释放NULL指针(安全操作)

C 标 准 规 定:对 NULL 指 针 调 用 free() 是 安 全 的,等 价 于 “什 么 都 不 做”,不 会 引 发 错 误。

         若 省 略 p = NULL,第 二 次 调 用 free( p ) 时,p 仍 指 向 已 释 放 的 内 存,属 于 重 复 释 放 同 一 内 存 块,会 导 致 未 定 义 行 为(如 程 序 崩 溃 )。

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

下 面 展 示代 码 示 例

#include <stdio.h>
#include <stdlib.h>
int main()
{int* p = (int*)malloc(100);p++;free(p);//free释放p指针的起始位置return 0;
}

         free 释 放 p 指 针 的 起 始 位 置,如 果 起 始 位 置 变 化,则 代 码 会 报 错,这 同 样 是 不 允 许 的。

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

下 面 展 示代 码 示 例

#include <stdio.h>
#include <stdlib.h>
int* test()
{int* p = (int*)malloc(100);if (NULL == p){return 1;}return p;
}
int main()
{int ptr = test();return 0;
}

         函 数 free 与 动 态 内 存 分 配 函 数 一 起 使 用,如 果 开 辟 空 间 完 之 后 没 有 释 放,则 会 造 成 空 间 占 用,直 到 程 序 结 束。

malloc 与 free 成 对 出 现,但 没 有 执 行

下 面 展 示代 码 示 例

#include <stdio.h>
#include <stdlib.h>
void test()
{int* p = (int*)malloc(100);if (NULL == p){return;}if (1)return;free(p);p = NULL;
}
int main()
{test();return 0;
}

         在 上 面 的 代 码 中,函 数 提 前 返 回 跳 过 free 语 句,导 致 空 间 没 有 释 放,使 空 间 浪 费。

柔 性 数 组

定 义

         在 C99 标 准 中,结 构 体 的 最 后 一 个 元 素 可 以 是 未 知 大 小 的 数 组, 这 便 是 柔 性 数 组 成 员。

下 面 展 示代 码 示 例

struct S
{ int i; int arr[]; // 柔性数组,不指定大小,或者写成int arr[0];
};

特 点

前 置 成 员 要 求:结 构 体 中 柔 性 数 组 成 员 前 面 必 须 至 少 有 一 个 其 他 成 员。

sizeof 特 性:sizeof 计 算 包 含 柔 性 数 组 的 结 构 体 大 小 时,不 会 包 含 柔 性 数 组 的 内 存。

内 存 分 配:需 使 用 malloc 等 函 数 进 行 动 态 内 存 分 配,且 分 配 的 内 存 要 大 于 结 构 体 本 身 大 小,以 容 纳 柔 性 数 组 预 期 数 据。

代 码 1

下 面 展 示代 码 示 例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct S
{int n;char arr[];//或者char arr[0];数组大小是未知的
};
int main()
{struct S* ps = (struct S*)malloc(sizeof(struct S) + 10 * sizeof(char));//数组大小可调整if (ps == NULL){perror("ps");return 1;}ps->n = 100;int i = 0;for (i = 0;i < 10;i++){ps->arr[i] = 'q' + i;}for (i = 0;i < 10;i++){printf("%c ", ps->arr[i]);}printf("\n");free(ps);ps = NULL;return 0;
}

在这里插入图片描述

代 码 2

下 面 展 示代 码 示 例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct S
{int n;char* arr;
};
int main()
{struct S* ps = (struct S*)malloc(sizeof(struct S));if (ps == NULL){perror("malloc");return 1;}ps->n = 100;ps->arr = (char*)malloc(sizeof(char) * 10);if (ps->arr == NULL){perror("malloc->arr");return 1;}int i = 0;for (i = 0;i < 10;i++){ps->arr[i] = 'q' + i;}for (i = 0;i < 10;i++){printf("%c ",ps->arr[i]);}printf("\n");free(ps->arr);ps->arr = NULL;free(ps);ps = NULL;return 0;
}

在这里插入图片描述

总 结

在 上 面 的 代 码 2 段 代 码 中,代 码 1 更 好。

方 便 内 存 释 放

         在 写 代 码 过 程 中 应 尽 可 能 减 少 结 构 体 变 量 的 动 态 内 存 分 配 次 数,最 好 只 使 用 一 次 free 就 可 以 释 放 内 存,避 免 内 存 泄 漏 等 问 题。

有 利 于 访 问 速 度

连 续 的 内 存 有 益 于 提 高 访 问 速 度,也 有 益 于 减 少 内 存 碎 片。

在这里插入图片描述

总结

         至 此,关 于 C 语 言 动 态 内 存 分 配 的 探 索 暂 告 一 段 落,但 你 的 编 程 征 程 才 刚 刚 启 航。写 代 码 是 与 机 器 深 度 对 话,过 程 中 虽 会 在 语 法、算 法 困 境 里 挣 扎,但 这 些 磨 砺 加 深 了 对 代 码 的 理 解。愿 你 合 上 电 脑 后,灵 感 不 断,在 C 语 言 的 世 界 里 持 续 深 耕,书 写 属 于 自 己 的 编 程 传 奇,下 一 次 开 启 ,定 有 全 新 的 精 彩 等 待。小 编 期 待 重 逢,盼 下 次 阅 读 见 你 们 更 大 进 步,共 赴 代 码 之 约!

http://www.xdnf.cn/news/3317.html

相关文章:

  • SIwave基本操作之S参数仿真
  • 5. 进程地址空间
  • react中封装一个预览.doc和.docx文件的组件
  • Vue3 + TypeScript 实现 PC 端鼠标横向拖动滚动
  • 【蓝桥杯】第十六届蓝桥杯C/C++大学B组个人反思总结
  • 高性能架构设计-数据库(读写分离)
  • OpenHarmony - 小型系统内核(LiteOS-A)(十七)标准库
  • 加速LLM大模型推理,KV缓存技术详解与PyTorch实现
  • java: 警告: 源发行版 21 需要目标发行版 21
  • PostgreSQL的COALESCE 函数用法
  • 慧星云支持 Qwen3:开启智算新生态,共筑高效 AI 未来
  • WebGL图形编程实战【5】:层次构建 × Shader初始化深度剖析
  • 基于ssm的校园旧书交易交换平台(源码+文档)
  • Microsoft Entra ID 详解:现代身份与访问管理的核心
  • 三分钟了解自动拆箱封箱操作
  • Pillow 移除或更改了 FreeTypeFont.getsize() 方法
  • mac下载homebrew 安装和使用git
  • SimFlow: 基于OpenFOAM的CFD求解器
  • 积木报表的 API 数据集 (附Demo图文)
  • JavaAPI — 日期与集合
  • Spring MVC @RequestParam 注解怎么用?如何处理可选参数和默认值?
  • 温补晶振(TCXO)稳定性优化:从实验室到量产的关键技术
  • 【爬虫】deepseek谈爬虫工具
  • Java 多线程进阶:什么是线程安全?
  • 如何在 Linux 环境下使用 Certbot 自动生成 SSL 证书并部署到 Nginx 服务中
  • 【论文阅读】APMSA: Adversarial Perturbation Against Model Stealing Attacks
  • 7.软考高项(信息系统项目管理师)-资源管理
  • C++初阶-string类2
  • [PRO_A7] SZ501 FPGA开发板简介
  • Roboflow标注数据集