C语言中使用动态内存

ops/2024/11/28 21:36:05/

在前面介绍C语言的内存模型时知道C语言中将内存划分为多个区间:栈区、堆区、静态区、常量区、代码区。在方法内定义和使用的变量,如果没有使用static关键字修饰都是在栈区内,该区内定义的变量不需要我们管理,系统会自动申请和释放空间。在内存空间划分中还有一个非常重要的堆区,在这个区间定义的变量需要程序自己管理空间的申请和释放,如果使用不当,就会造成内存泄露问题,严重的更可能导致程序崩溃。
内存使用

C语言动态操作内存首先要引入 stdlib.h 库,涉及到内存操作的函数有下面这些:
(1)malloc:动态分配一块内存空间,该函数的形参是要分配的内存空间大小,单位是字节。函数的返回值是一个void类型的指针,在使用时需要转换为具体类型的指针。函数原型是:

void * malloc(size_t _Size);

通过函数定义可以知道该函数只是返回内存的首地址,没有总内存大小,如果要在后面使用内存大小最好自己定义一个变量记录下来;malloc函数申请的空间也不会初始化,在赋值前访问内存空间是脏数据;申请过多的内存空间后,会产生虚拟内存;注意申请的内存要及时释放,避免内存泄露的风险。
函数使用示例:

#include <stdio.h>
#include <stdlib.h>int main() {// 申请存放10个整型数据的内存int* p = (int *) malloc(10 * sizeof(int));// 遍历申请的内存空间输出数据printf("---------------- uninitialized ----------------\n");for (int i = 0; i < 10; i++) {printf("%d\n", *(p + i));}// 内存赋值for (int i = 0; i < 10; i++) {*(p + i) = (i * 10);}// 遍历申请的内存空间输出数据printf("---------------- initialization ----------------\n");for (int i = 0; i < 10; i++) {printf("%d\n", *(p + i));}// 释放内存空间free(p);return 0;
}

程序运行输出:

---------------- uninitialized ----------------
-1023344304
508
-1023344304
508
778857573
1886350710
1852795252
1917845619
1634887535
875976557
---------------- initialization ----------------
0
10
20
30
40
50
60
70
80
90

malloc输出
(2)calloc:也是用于动态分配内存,该函数两个形参分别是元素数量以及每个元素所占的字节大小。与malloc不同的是,它会在分配内存后初始化为0。由于有初始化的操作,整体来说性能不如malloc。函数原型是:

void * calloc(size_t _NumOfElements, size_t _SizeOfElements);

函数使用示例:

#include <stdio.h>
#include <stdlib.h>int main() {// 申请存放10个整型数据的内存int* p = (int *) calloc(10, sizeof(int));// 遍历申请的内存空间输出数据printf("---------------- uninitialized ----------------\n");for (int i = 0; i < 10; i++) {printf("%d\n", *(p + i));}// 内存赋值for (int i = 0; i < 10; i++) {*(p + i) = (i * 10);}// 遍历申请的内存空间输出数据printf("---------------- initialization ----------------\n");for (int i = 0; i < 10; i++) {printf("%d\n", *(p + i));}// 释放内存空间free(p);return 0;
}

程序运行输出:

---------------- uninitialized ----------------
0
0
0
0
0
0
0
0
0
0
---------------- initialization ----------------
0
10
20
30
40
50
60
70
80
90

calloc函数
(3)realloc:用于更改已经分配的内存大小,函数有两个形参,第一个是指向已经分配的内存地址指针,第二个参数是新的内存大小。函数的返回值也是一个void类型的指针。函数原型是:

void * realloc(void *_Memory, size_t _NewSize);

函数使用示例:

#include <stdio.h>
#include <stdlib.h>int main() {int size = 10;// 申请存放10个整型数据的内存int* p = (int *) malloc(size * sizeof(int));// 内存赋值for (int i = 0; i < size; i++) {*(p + i) = (i * 10);}// 遍历申请的内存空间输出数据printf("---------------- initialization ----------------\n");for (int i = 0; i < size; i++) {printf("%d\n", *(p + i));}// 内存扩容size = 12;int* p1 = (int *) realloc(p, size * sizeof(int));if (p1 == NULL) {// 扩容失败后需要手动释放原来的内存空间free(p);}// 遍历释放后的内存空间printf("---------------- after realloc ----------------\n");for (int i = 0; i < size; i++) {printf("%d\n", *(p1 + i));}// 释放内存空间free(p1);return 0;
}

程序运行输出:

---------------- initialization ----------------
0
10
20
30
40
50
60
70
80
90
---------------- after realloc ----------------
0
10
20
30
40
50
60
70
80
90
1247569260
1549891169

调用realloc函数如果扩容成功,原内存空间会自动处理,如果只是在原内存空间继续追加大小,内存地址会继续使用;如果原内存空间不能继续扩容,该函数会重新申请一块内存空间,并将原空间数据复制到新内存空间中,原来的内存空间会释放。如果扩容失败,原来的内存空间不会释放,这时就需要手动释放原内存空间。
realloc函数
由于内存空间充足,几次调用扩容函数都是在原地址上追加:

#include <stdio.h>
#include <stdlib.h>int main() {int size = 10;// 申请存放10个整型数据的内存int* p = (int *) malloc(size * sizeof(int));// 内存赋值for (int i = 0; i < size; i++) {*(p + i) = (i * 10);}printf("---------------- malloc ----------------\n");printf("malloc memory address : %p\n", p);for (int i = 0; i < size; i++) {printf("%d\n", *(p + i));}// 内存扩容int* p1 = (int *) realloc(p, (size + 400) * sizeof(int));if (p1 == NULL) {// 扩容失败后需要手动释放原来的内存空间free(p);}// 遍历释放后的内存空间printf("---------------- after realloc ----------------\n");printf("realloc memory address : %p\n", p);for (int i = 0; i < size; i++) {printf("%d\n", *(p1 + i));}// 释放内存空间free(p1);return 0;
}

程序运行输出:

---------------- malloc ----------------
malloc memory address : 0000028a23cb1650
0
10
20
30
40
50
60
70
80
90
---------------- after realloc ----------------
realloc memory address : 0000028a23cb1650
0
10
20
30
40
50
60
70
80
90

realloc函数2
(4)free:用于释放动态分配的内存,函数的形参是指向要释放的内存地址。函数原型是:

void free(void *_Memory);

free函数释放后的内存空间中的数据是脏数据,可能被其他程序使用或清空,读取释放后的内存数据没有意义。
函数使用示例:

#include <stdio.h>
#include <stdlib.h>int main() {// 申请存放10个整型数据的内存int* p = (int *) malloc(10 * sizeof(int));// 内存赋值for (int i = 0; i < 10; i++) {*(p + i) = (i * 10);}// 遍历申请的内存空间输出数据printf("---------------- initialization ----------------\n");for (int i = 0; i < 10; i++) {printf("%d\n", *(p + i));}// 释放内存空间free(p);// 遍历释放后的内存空间printf("---------------- after free ----------------\n");for (int i = 0; i < 10; i++) {printf("%d\n", *(p + i));}return 0;
}

程序运行输出:

---------------- initialization ----------------
0
10
20
30
40
50
60
70
80
90
---------------- after free ----------------
-2109137856
691
-2109144752
691
40
50
60
70
80
90

使用堆内存性能是不如栈内存的,并且还存在内存泄露的风险。但是有些场景是栈内存无法满足的,比如要存储很多数据的大数组,链表结构的数据。还有方法的返回值,虽然可以使用指针或static修饰变量返回,但他们都有局限性:使用指针需要在方法形参传递过去,要提前初始化好内存空间,这种方式不方便扩展;使用static修饰变量存放在全局数据区,会导致整个程序生命周期内该变量都存在,即使数据不使用了也不会删除,浪费内存空间,所以方法返回数据使用动态内存是一个很好的选择。


http://www.ppmy.cn/ops/137479.html

相关文章

【设计模式】1. 构建器模式(Builder Pattern)是一种创建型设计模式

构建器模式&#xff08;Builder Pattern&#xff09;是一种创建型设计模式&#xff0c;用于分步骤构建复杂对象&#xff0c;同时允许按照不同的需求生成不同的表示。该模式将对象的构建过程与其表示分离&#xff0c;使得相同的构建过程可以创建不同的对象。 核心思想 构建器模…

Go 中的并发 Map:深入探索 sync.Map 及其他实现方法

在 Go 语言的并发编程世界中&#xff0c;数据共享和同步是永恒的话题。map 是 Go 语言中常用的数据结构&#xff0c;但在多 goroutine 环境中直接使用它并不是线程安全的。因此&#xff0c;我们需要采用特定的策略来确保并发访问的安全性。本文将深入探讨 Go 中的并发 Map&…

Android 设备使用 Wireshark 工具进行网络抓包

背景 电脑和手机连接同一网络&#xff0c;想使用wireshark抓包工具抓取Android手机网络日志&#xff0c;有以下两种连接方法&#xff1a; Wi-Fi 网络抓包。USB 网络共享抓包。需要USB 数据线将手机连接到电脑&#xff0c;并在开发者模式中启用 USB 网络共享。 查看设备连接信…

Design Linear Filters in the Frequency Domain (MATLAB帮助文档)

Design Linear Filters in the Frequency Domain 这个帮助文档写得很好&#xff0c;简单明了&#xff0c;一句废话没有。 This topic describes functions that perform filtering in the frequency domain. 2-D Finite Impulse Response (FIR) Filters The Image Processi…

HTML 元素类型介绍

目录 1. 块级元素&#xff08;Block-level Elements&#xff09; 2. 行级元素&#xff08;Inline Elements&#xff09; 3. 行内块级元素&#xff08;Inline-block Elements&#xff09; 4. 表格相关元素 5. 列表相关元素 6. 表单相关元素 示例代码 示例效果 ​编辑 …

医疗数据质量安全,数据安全解决方案,医院关心的数据安全问题,信息安全方案(Word原件)

一、 数据安全背景 二、 风险分析 三、 解决方案 3.1方案设计 3.2敏感数据分类分级 3.4数据安全防御体系 3.4.1访问控制防护 3.4.2网络可信接入 3.4.3应用身份识别 3.4.4抵御SQL注入 3.4.5虚拟补丁防护 3.4.6阻断漏洞攻击 3.4.7权限细粒度管理 3.5规范数据运维管理…

华为昇腾 acl_pytorch

目录 sam部署教程&#xff1a; segment-anyghing地址&#xff1a; sam onnx地址&#xff1a; 报错&#xff1a; encode继续报错&#xff1a; sam部署教程&#xff1a; Ascend/ModelZoo-PyTorch - Gitee.com segment-anyghing地址&#xff1a; https://github.com/visher…

如何在 Ubuntu 22 04 上安装和配置 Ansible 自动化平台

如何在 Ubuntu 22.04 上安装和配置 Ansible 自动化平台 简介 Ansible 是一个开源项目&#xff0c;并在 Github 上收获了 63k 的 star 。它是一个极其简单的 IT 自动化平台&#xff0c;使您的应用程序和系统更易于部署和维护。使用 SSH&#xff0c;以接近简单英语的语言实现从…