C语言内存之旅:从静态到动态的跨越

大家好,这里是小编的博客频道
小编的博客:就爱学编程

很高兴在CSDN这个大家庭与大家相识,希望能在这里与大家共同进步,共同收获更好的自己!!!

本文目录

  • 引言
  • 正文
    • 一 动态内存管理的必要性
    • 二 动态内存管理的关键函数
      • 1.`malloc`函数
      • 2.`calloc`函数
      • 3.`realloc`函数
      • 4.`free`函数
    • 三 动态内存管理中的错误和最佳实践
      • 1.内存泄漏
      • 2.野指针
      • 3.内存越界
    • 四 动态内存管理的高级主题
      • 内存分配器
      • 内存池
      • 垃圾收集
  • 快乐的时光总是短暂,咱们下篇博文再见啦!!!不要忘了,给小编点点赞和收藏支持一下,在此非常感谢!!!

引言

动态内存管理是程序设计中用于在程序运行时分配和释放内存的机制。这种管理方式允许程序根据实际需要动态地调整内存使用,从而更有效地利用系统资源。所以就让小编来对动态内存管理做一个详细的介绍。

在这里插入图片描述


那接下来就让我们开始遨游在知识的海洋!

正文


首先让我们来了解一下动态内存管理的必要性

一 动态内存管理的必要性

在静态内存分配中,内存的分配和释放由编译器自动管理,通常发生在栈(stack)上。例如,局部变量的分配和释放就是静态的,它们的生命周期仅限于函数调用的开始和结束。

动态内存管理的必要性主要体现在以下几个方面:

  • 可变大小的数据结构:对于一些数据结构,如动态数组、链表、树、图等,其大小在编译时无法确定,需要在运行时根据实际需要进行分配。

  • 优化内存使用:动态内存管理可以根据程序的实际需求分配内存,避免浪费,提高内存使用效率。

  • 跨函数或代码块的数据生命周期:有些数据需要在多个函数或代码块中使用,其生命周期超出了局部作用域,动态内存管理可以满足这种需求。


知道了动态内存管理的必要性,我们就迎来了动态内存管理最重要的核心内容:二 动态内存管理的关键函数。

二 动态内存管理的关键函数

在C和C++语言中,动态内存管理主要通过以下几个标准库函数实现。


1.malloc函数

malloc是 C 语言中用于动态内存分配的函数,它允许程序在运行时申请一块指定大小的内存空间。这个函数的特点是它不初始化内存内容,即保留之前使用过的数据。

函数原型:

void* malloc(size_t size);
  • size参数是要申请的内存大小,单位是字节。

  • 返回值是void*类型,指向分配的内存块的起始地址。如果分配失败,返回NULL

示例代码:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int* dynamicArray = (int*)malloc(5 * sizeof(int)); // 分配5个int大小的内存
    if (dynamicArray == NULL) {
        printf("Memory allocation failed.\n");
        return 1;
    }
    // 使用分配的内存
    for (int i = 0; i < 5; ++i) {
        dynamicArray[i] = i;
    }
    // 打印数组内容
    for (int i = 0; i < 5; ++i) {
        printf("%d ", dynamicArray[i]);
    }
    printf("\n");
    // 使用完毕后释放内存
    free(dynamicArray);
    return 0;
}

在上述示例中,我们分配了足够存放5个整数的内存空间,并检查了malloc返回的指针是否为NULL,以确保内存分配成功。


2.calloc函数

calloc函数与malloc类似,但它会将分配的内存初始化为零。这对于需要清零的数组或结构体非常有用。

函数原型:

void* calloc(size_t num, size_t size);
  • num参数是元素的数量。

  • size参数是每个元素的大小,单位是字节。

  • 返回值是void*类型,指向分配并初始化为零的内存块的起始地址。如果分配失败,返回NULL

示例代码:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int* dynamicArray = (int*)calloc(5, sizeof(int)); // 分配5个int大小的内存,并初始化为0
    if (dynamicArray == NULL) {
        printf("Memory allocation failed.\n");
        return 1;
    }
    // 使用分配的内存
    for (int i = 0; i < 5; ++i) {
        printf("%d ", dynamicArray[i]); // 将打印5个0
    }
    printf("\n");
    // 使用完毕后释放内存
    free(dynamicArray);
    return 0;
}

在上述示例中,我们分配了足够存放5个整数的内存空间,并且这些整数都被初始化为0。


3.realloc函数

realloc函数用于调整之前通过malloccalloc分配的内存块的大小。如果调整后的内存块变大,新增加的部分内容是未定义的;如果变小,超出新大小的数据可能会被截断。

函数原型:

void* realloc(void* ptr, size_t new_size);
  • ptr参数是之前分配的内存块的指针。

  • new_size参数是新的内存大小,单位是字节。

  • 返回值是void*类型,指向调整后的内存块的起始地址。如果调整失败,返回NULL

示例代码:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int* dynamicArray = (int*)malloc(3 * sizeof(int)); // 初始分配3个int的空间
    if (dynamicArray == NULL) {
        printf("Memory allocation failed.\n");
        return 1;
    }
    // 假设我们需要更多的空间
    dynamicArray = (int*)realloc(dynamicArray, 6 * sizeof(int)); // 调整为6个int的空间
    if (dynamicArray == NULL) {
        printf("Memory reallocation failed.\n");
        return 1;
    }
    // 使用调整后的内存
    for (int i = 0; i < 6; ++i) {
        dynamicArray[i] = i;
    }
    // 打印数组内容
    for (int i = 0; i < 6; ++i) {
        printf("%d ", dynamicArray[i]);
    }
    printf("\n");
    // 使用完毕后释放内存
    free(dynamicArray);
    return 0;
}

在上述示例中,我们首先分配了足够存放3个整数的内存空间,然后使用realloc将其扩展到足够存放6个整数的空间。


4.free函数

free函数用于释放之前通过malloccallocrealloc分配的内存。释放内存后,指针不再有效,不应再被使用。

函数原型:

void free(void* ptr);

ptr参数是之前分配的内存块的指针。

示例代码:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int* dynamicArray = (int*)malloc(5 * sizeof(int)); // 分配5个int大小的内存
    if (dynamicArray == NULL) {
        printf("Memory allocation failed.\n");
        return 1;
    }
    // 使用分配的内存
    for (int i = 0; i < 5; ++i) {
        dynamicArray[i] = i;
    }
    // 打印数组内容
    for (int i = 0; i < 5; ++i) {
        printf("%d ", dynamicArray[i]);
    }
    printf("\n");
    // 释放内存
    free(dynamicArray);
    dynamicArray = NULL; // 避免野指针
    return 0;
}

在上述示例中,我们分配了内存,并在使用完毕后通过free释放了它。释放后,我们将指针设置为NULL,以避免产生野指针。

这些函数是动态内存管理的基础,它们使得程序能够灵活地处理内存,适应不同的运行时需求。正确使用这些函数对于避免内存泄漏和其他内存相关的问题至关重要。希望这些介绍能够满足宝子们对动态内存管理详细了解的需求。


三 动态内存管理中的错误和最佳实践

1.内存泄漏

内存泄漏发生在程序分配了内存但未能释放它,导致程序在运行过程中占用越来越多的内存。为了避免内存泄漏,可以遵循以下最佳实践:

  • 确保每次malloc都配对相应的free:每次使用malloc分配内存后,必须在不再需要该内存时调用free

  • 使用智能指针(C++):智能指针如std::unique_ptrstd::shared_ptr可以自动管理内存,减少内存泄漏的风险。

  • 自定义内存管理策略:在性能要求高或特定环境下,开发者可能需要实现自定义的内存管理策略,如内存池。


2.野指针

野指针是指指向已经被释放内存的指针。使用野指针可能导致程序崩溃或数据损坏。为了避免野指针,可以采取以下措施:

  • 释放内存后将指针设置为NULL:这是一个好习惯,可以避免意外地使用已经释放的内存。

  • 使用工具检测内存错误:使用如 Valgrind 这样的工具可以帮助检测内存泄漏和野指针等错误。


3.内存越界

内存越界是指访问分配的内存之外的区域,这可能导致程序崩溃或数据损坏。为了避免内存越界,可以采取以下措施:

  • 仔细检查数组索引和内存块边界:在访问数组或内存块时,始终检查索引或指针是否超出范围。

  • 使用安全编码实践:如初始化指针为NULL,使用边界检查等。


四 动态内存管理的高级主题

内存分配器

一些高级应用可能需要自定义内存分配器,以优化特定类型的内存分配模式。自定义内存分配器可以减少内存碎片,提高内存分配和释放的效率。


内存池

在性能敏感的应用中,使用内存池可以减少内存分配和释放的开销。内存池预先分配一大块内存,并在需要时从池中分配小块内存,释放时返回到池中,而不是直接释放到操作系统。


垃圾收集

在一些高级语言中(如Java和C#),垃圾收集器自动管理内存,减少了程序员的负担。垃圾收集器通过跟踪对象的引用来确定哪些内存可以被释放。

结论

  • 动态内存管理是程序设计中的一个重要组成部分,它为程序提供了灵活性和效率,允许程序在运行时根据需要分配和释放内存。然而,这也带来了内存泄漏等风险,因此需要开发者谨慎管理内存,遵循最佳实践,以确保程序的稳定性和性能。

以上是对动态内存管理的详细介绍,包括其基本概念、必要性、技术细节、应用场景以及相关的错误和最佳实践。希望这个介绍能够满足宝子们对动态内存管理详细了解的需求。如果宝子们有其他问题或需要进一步的解释,请随时告诉小编。


快乐的时光总是短暂,咱们下篇博文再见啦!!!不要忘了,给小编点点赞和收藏支持一下,在此非常感谢!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/956992.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[STM32 HAL库]串口中断编程思路

一、前言 最近在准备蓝桥杯比赛&#xff08;嵌入式赛道&#xff09;&#xff0c;研究了以下串口空闲中断DMA接收不定长的数据&#xff0c;感觉这个方法的接收效率很高&#xff0c;十分好用。方法配置都成功了&#xff0c;但是有一个点需要进行考虑&#xff0c;就是一般我们需要…

PyTorch使用教程(10)-torchinfo.summary网络结构可视化详细说明

1、基本介绍 torchinfo是一个为PyTorch用户量身定做的开源工具&#xff0c;其核心功能之一是summary函数。这个函数旨在简化模型的开发与调试流程&#xff0c;让模型架构一目了然。通过torchinfo的summary函数&#xff0c;用户可以快速获取模型的详细结构和统计信息&#xff0…

Java模拟路由协议-rip(路由器仿真实验)

前言&#xff1a; 好久不见&#xff0c;有段时间没有写文章了&#xff0c;本篇文章&#xff0c;由Blue我带大家来复现rip协议。我们以 b站湖南教师匠所讲rip的视频中的例子为我这篇文章所模拟的路由路径 如图&#xff1a; 模拟路径 视频&#xff1a;http://【深入浅出计算机网络…

32V/4A,降压DCDC转换器CP8384百分百占空比输出ESOP8封装,可适用HUB等电路设计

特点&#xff1a; ● Supply Voltage Range: 4.1V~32V ● Input voltage up to 40V ● 4A Continuous Output Current ● Up to 95% Output Efficiency ● CC/CV control ● 350kHz Switching Frequency ● Built-in Soft Start ● 100% Maximum Duty Cycle ● No External Com…

缓存、数据库双写一致性解决方案

双写一致性问题的核心是确保数据库和缓存之间的数据同步&#xff0c;以避免缓存与数据库数据不同步的问题&#xff0c;尤其是在高并发和异步环境下。本文将探讨双写一致性面临的主要问题和解决方案&#xff0c;重点关注最终一致性。 本文讨论的是最终一致性问题 双写一致性面…

【学习笔记15】如何在非root服务器中,安装属于自己的redis

一、下载安装包 官网下载黑马程序员给的安装包&#xff08;redis-6.2.6&#xff09; 二、将安装包上传至服务器 我将安装包上传在我的文件夹/home/XXX&#xff0c;指定路径中/src/local/redis/&#xff0c;绝对路径为/home/XXX/src/local/redis/解压安装包 XXXomega:~$ cd …

计算机网络 (51)鉴别

前言 计算机网络鉴别是信息安全领域中的一项关键技术&#xff0c;主要用于验证用户或信息的真实性&#xff0c;以及确保信息的完整性和来源的可靠性。 一、目的与重要性 鉴别的目的是验明用户或信息的正身&#xff0c;对实体声称的身份进行唯一识别&#xff0c;以便验证其访问请…

【大模型】ChatGPT 高效处理图片技巧使用详解

目录 一、前言 二、ChatGPT 4 图片处理介绍 2.1 ChatGPT 4 图片处理概述 2.1.1 图像识别与分类 2.1.2 图像搜索 2.1.3 图像生成 2.1.4 多模态理解 2.1.5 细粒度图像识别 2.1.6 生成式图像任务处理 2.1.7 图像与文本互动 2.2 ChatGPT 4 图片处理应用场景 三、文生图操…

后端:MyBatis

文章目录 1. MyBatis1-1. Mybatis 工具类的封装1-2. Mybatis 通过集合或实体类传递参数-实现插入数据(增)1-3. MyBatis 实现删除数据(删)1-4. MyBatis 实现修改数据(改)1-5. MyBatis 实现查询数据(查) 2. MyBatis 配置文件中的一些标签和属性2-1.environments标签2-2. dataSour…

将 AzureBlob 的日志通过 Azure Event Hubs 发给 Elasticsearch(1.标准版)

问题 项目里使用了 AzureBlob 存储了用户上传的各种资源文件&#xff0c;近期 AzureBlob 的流量费用增长很快&#xff0c;想通过分析Blob的日志&#xff0c;获取一些可用的信息&#xff0c;所以有了这个需求&#xff1a;将存储账户的日志&#xff08;读写&#xff0c;审计&…

数字化时代,传统代理模式的变革之路

在数字化飞速发展的今天&#xff0c;线上线下融合&#xff08;O2O&#xff09;成了商业领域的大趋势。这股潮流&#xff0c;正猛烈冲击着传统代理模式&#xff0c;给它带来了新的改变。 咱们先看看线上线下融合现在啥情况。线上渠道那是越来越多&#xff0c;企业纷纷在电商平台…

【AI | pytorch】torch.polar的使用

一、torch.polar的使用 torch.polar 是 PyTorch 中用来生成复数张量的一个函数&#xff0c;但它与数学中的复数表达式 ( z re^{i\theta} ) 是等价的。 具体来说&#xff0c;torch.polar(abs, angle) 接受两个实数张量参数&#xff1a; abs&#xff1a;表示复数的模长&#…

LeetCode 110.平衡二叉树

题目描述 给定一个二叉树&#xff0c;判断它是否是平衡二叉树。 示例 1&#xff1a; 示例 2&#xff1a; 输入&#xff1a;root [1,2,2,3,3,null,null,4,4] 输出&#xff1a;false 示例 3&#xff1a; 输入&#xff1a;root [] 输出&#xff1a;true 提示&#xff1a; …

数据结构(Java版)第四期:ArrayLIst和顺序表(上)

目录 一、顺序表 1.1. 接口的实现 二、ArrayList简介 2.1. ArrayList的构造 2.2. ArrayList的常见操作 2.3. ArrayList的扩容机制 三、ArrayList的具体使用 3.1. 洗牌算法 3.2. 杨辉三角 一、顺序表 上一期我们讲到过&#xff0c;顺序表本质上和数组是差不多的&#…

阿里云 Serverless 助力盟主直播:高并发下的稳定性和成本优化

在直播场景中&#xff0c;阿里云 Serverless 应用引擎 SAE 提供的无缝弹性伸缩与极速部署能力&#xff0c;确保直播间高并发时的流畅体验&#xff0c;降低了我们的运营成本&#xff0c;简化了运维流程。结合阿里云云原生数据库 PolarDB 的 Serverless 能力&#xff0c;实现了数…

【机器学习实战入门】基于深度学习的乳腺癌分类

什么是深度学习&#xff1f; 作为对机器学习的一种深入方法&#xff0c;深度学习受到了人类大脑和其生物神经网络的启发。它包括深层神经网络、递归神经网络、卷积神经网络和深度信念网络等架构&#xff0c;这些架构由多层组成&#xff0c;数据必须通过这些层才能最终产生输出。…

Qt之QDjango-db的简单使用

QDjango是一款由C编写、依托于Qt库的Web开发框架&#xff0c;其设计理念受到了广受欢迎的Python框架Django的影响。这个项目旨在提供一个高效、灵活且易于使用的工具集&#xff0c;帮助开发者构建高质量的Web应用。其项目地址: https://gitcode.com/gh_mirrors/qd/qdjango&…

[2025分类时序异常检测指标R-AUC与VUS]

梳理了一下分类中常见的指标&#xff0c;这些指标与时序异常检测中新提出的A-RUC与VUS之间的关系 真正例(True Positive,TP): 被正确识别为正样本的数量。真负例(True Negative,TN): 被正确识别为负样本的数量。假正例(False Positive ,FP): 被错误识为正样本数量假负例(Fals…

python3GUI--仿崩坏三二次元登录页面(附下载地址) By:PyQt5

文章目录 一&#xff0e;前言二&#xff0e;预览三&#xff0e;实现方案1.实现原理1.PyQt52. 具体实现 2.UI设计1.UI组件化、模块化2.UI设计风格思路 3.项目代码结构4.使用方法3.代码分享1.支持跳转网页的QLabel组件2.三角形ICON按钮 四&#xff0e;总结 大小&#xff1a;33.3 …

STM32 FreeRTOS中断管理

目录 FreeRTOS的中断管理 1、STM32中断优先级管理 2、FreeRTOS任务优先级管理 3、寄存器和内存映射寄存器 4、BASEPRI寄存器 5、FreeRTOS与STM32中断管理结合使用 vPortRaiseBASEPRI vPortSetBASEPRI 6、FromISR后缀 7、在中断服务函数中调用FreeRTOS的API函数需注意 F…