【C语言】内存的动态分配与释放

news2025/2/26 2:08:39

🦄个人主页:修修修也

🎏所属专栏:C语言

⚙️操作环境:Visual Studio 2022


目录

什么是内存的动态分配?

内存动态分配函数

🎏malloc()

🎏calloc()

🎏realloc()

动态内存释放函数

🎏free()

常见的动态内存错误

1.对NULL指针的解引用操作

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

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

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

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

6.动态开辟内存忘记释放

结语


什么是内存的动态分配?

要知道什么是内存的动态分配,首先要清楚内存在计算机中内存是如何划分的:

如图,内存区域大致分为以下几个区域:

  • 栈区(向下增长)(stack):由编译器自动分配释放,存放:局部变量,形参,返回值.
  • 堆区(向上增长)(heap):由程序员分配内存和释放.通过调用函数:malloc(),calloc(),realloc()和free().
  • 全局(静态)区:未初始化全局/静态区(.bass)和已初始化全局/静态区(.data).
  • 常量区(.rodata):字符串"ABCD"等

  • 代码区(.text):存放程序的代码

我们从前的内存使用方式是,比如创建一个变量:

int a=10;

这时变量是存储在栈区的,是由编译器自动分配的.

再比如我们创建一个数组,如:

int arr[10]={0};

这时数组内容仍然存储在栈区中,由编译器分配空间存储或销毁.

这样的内存使用方式有两个特点:

  1. 内存空间开辟大小是固定的.
  2. 数组在声明的时候,必须指定数组的长度,它所需要的内存在编译时分配.

这样的特点就导致了,我们无法在程序运行中的任意时刻分配存储空间,也不能把不需要的存储空间释放或丢弃.为了能够满足上述需求,我们就需要使用内存的动态分配.


内存动态分配函数

用于分配存储空间的两个函数是malloc()和calloc()函数,用于更改已分配空间的函数是realloc()函数,以下列出了这几个函数的相关信息:

🎏malloc()

malloc
头文件#include<stdlib.h>
格式void * malloc(size_t size);
功能大小为size字节的对象分配存储空间,此存储空间中的初始值不确定
返回值若分配成功,则返回一个指向已分配的空间开头的指针;若分配失败,则返回空指针

如果想了解更多关于malloc()函数相关信息,如malloc()函数参数的设定,返回值的设定,以及malloc()函数的具体使用方法等相关知识的,可以移步这里:

【C语言】malloc()函数详解(动态内存开辟函数)icon-default.png?t=N7T8https://blog.csdn.net/weixin_72357342/article/details/133971625?spm=1001.2014.3001.5502


🎏calloc()

calloc
头文件#include<stdlib.h>
格式void * calloc(size_t num,size_t size);
功能num个大小为size字节的对象分配存储空间,该空间内的所有位都会初始化为0
返回值若分配成功,则返回一个指向已分配的空间开头的指针;若分配失败,则返回空指针

 如果想了解更多关于calloc()函数相关信息,如calloc()函数参数的设定,返回值的设定,以及calloc()函数的具体使用方法等相关知识的,可以移步这里:

【C语言】calloc()函数详解(动态内存开辟函数)icon-default.png?t=N7T8https://blog.csdn.net/weixin_72357342/article/details/133975677


🎏realloc()

realloc
头文件#include<stdlib.h>
格式void * realloc(void* ptr , size_t size);
功能更改ptr指向的已分配空间的大小,重新分配为size大小
返回值若分配成功,则返回一个指向已分配的空间开头的指针;若分配失败,则返回空指针

如果想了解更多关于realloc()函数相关信息,如realloc()函数参数的设定,返回值的设定,以及realloc()函数的具体使用方法等相关知识的,可以移步这里: 

【C语言】realloc()函数详解(动态内存开辟函数)icon-default.png?t=N7T8https://blog.csdn.net/weixin_72357342/article/details/133975646


动态内存释放函数

🎏free()

free
头文件#include<stdlib.h>
格式void * free(void* ptr);
功能释放ptr指向的空间,让这部分空间能继续用于之后的动态分配.当ptr为空指针时,不执行任何操作.除此之外,当实际参数与之前通过malloc(),calloc(),realloc()返回的指针不一致时,或者ptr指向的空间已经通过调用free()或realloc()被释放时,则作未定义处理.
返回值

如果想了解更多关于free()函数相关信息的,如free()函数参数的设定,返回值的设定,以及free()函数的具体使用方法等相关知识的,可以移步这里:

【C语言】free()函数详解(动态内存释放函数)icon-default.png?t=N7T8https://blog.csdn.net/weixin_72357342/article/details/133975657


常见的动态内存错误

1.对NULL指针的解引用操作

在这里先为大家介绍一种打印错误码的方法:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<stdio.h>

int main()
{
    int* p = (int*)malloc(INT_MAX);
    if (p == NULL)       //如果开辟失败,则打印错误
    {
        //打印错误原因的一个方式
        printf("%s\n", strerror(errno));
    }
    else
    {
        printf("开辟成功\n");
        //可以正常使用p指针来操作这片空间了
    }

    return 0;
}

 我们在vs编译器中测试一下这段代码:

可以看到strerror成功打印了错误信息.

再来看这段代码:

void test()
{
    int *p = (int *)malloc(INT_MAX);

    *p = 20;//如果p的值是NULL,就会有问题

    free(p);
}

由malloc()的定义可知,当malloc遇到没有足够的空间用来开辟的情况时,就会开辟失败,返回一个空指针.

而当我们不对malloc()函数开辟的结果做检查的话,就很可能导致以下这种情况:

因此,为防止在使用动态内存开辟函数时造成对空指针的解引用操作,我们在每次使用完动态内存开辟函数后,都应先检查一下它的返回值.


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

如下代码:

void test()
{
    int i = 0;
    int* p = (int*)malloc(10 * sizeof(int));
    if (NULL == p)
    {
        //打印错误原因的一个方式
        printf("%s\n", strerror(errno));
    }
    for (i = 0; i <= 10; i++)
    {
        *(p + i) = i;//当i是10的时候会造成越界访问
    }
    free(p);
}

在vs2022中测试该代码:

可以看到,编译器直接报错"检测到堆损坏".像这种报错不论是说栈区损坏,还是堆区损坏,意思就是在栈上或堆上出现了越界访问的情况.

因此,在使用动态内存开辟空间时,我们要格外小心不要出现越界访问的问题.


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

因为p是由编译器分配到栈区的,不属于堆区,因此不能使用free释放.

void test()
{
    int a = 10;
    int *p = &a;
    free(p);      //p不是动态开辟的,不能释放
}

 使用vs2022测试一下:

可以看到,该错误导致了程序出错.

而图中的报错"已执行断点指令"则是因为代码执行过程中出现了未定义的非法行为.


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

如下代码:

void test()
{
    int *p = (int *)malloc(100);
    p++;
    free(p);        //p不再指向动态内存的起始位置
}

 在vs2022中测试一下:

可以看到,该错误导致了程序异常终止.


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

如下代码:

void test()
{
    int *p = (int *)malloc(100);
    free(p);
    free(p);     //重复释放
}

 在vs2022中进行测试:

可以看到,该错误导致了程序出错.

这里列出了两个防止重复释放的小技巧:

  • 在设计时尽量遵从:谁开辟,谁回收的原则
  • 在free完后立刻将原动态开辟的指针置为NULL.

6.动态开辟内存忘记释放

如下代码:

void test()
{
    int *p = (int *)malloc(100);
    if(NULL != p)
    {
        *p = 20;
    }
    //没有释放!
}

int main()
{
    test();
}

 如果动态开辟的内存忘记释放,程序不会报错,但会造成内存泄漏!

忘记释放不再使用的动态开辟的空间会造成内存泄漏.

内存泄漏:如果动态开辟的内存没有被释放,那么这些内存就会一直占用系统资源,从而导致内存泄漏。内存泄漏会导致程序运行速度变慢,甚至崩溃。

因此:

动态开辟的空间一定要释放,并且正确释放!

动态开辟的空间一定要释放,并且正确释放!

动态开辟的空间一定要释放,并且正确释放!


结语

希望这篇内存的动态分配相关内容能对大家有所帮助,欢迎大佬们留言或私信与我交流.

学海漫浩浩,我亦苦作舟!关注我,大家一起学习,一起进步!

相关文章推荐

【C语言】malloc()函数详解(动态内存开辟函数)

【C语言】realloc()函数详解(动态内存开辟函数)

【C语言】calloc()函数详解(动态内存开辟函数)

【C语言】free()函数详解(动态内存释放函数)

【C语言】memcpy()函数

【数据结构实战项目】C语言实现数据结构顺序表万字详解(附完整运行代码)

【实用编程技巧】不想改bug?初学者必须学会使用的报错函数assert!(断言函数详解)



C语言动态内存开辟相关库函数思维导图:

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

轻量封装WebGPU渲染系统示例<5>-多重纹理(源码)

当前示例源码github地址: https://github.com/vilyLei/voxwebgpu/blob/version-1.01/src/voxgpu/sample/MultiTexturedCube.ts 此示例渲染系统实现的特性: 1. 用户态与系统态隔离。 2. 高频调用与低频调用隔离。 3. 面向用户的易用性封装。 4. 渲染数据和渲染机制分离。 …

【2023Mathorcup大数据】B题 电商零售商家需求预测及库存优化问题 python代码解析

【2023Mathorcup大数据】B题 电商零售商家需求预测及库存优化问题 python代码解析 1 题目 2023 年MathorCup 高校数学建模挑战赛——大数据竞赛赛道B&#xff1a;电商零售商家需求预测及库存优化问题电商平台存在着上千个商家&#xff0c;他们会将商品货物放在电商配套的仓库…

ChineseChess1 2023.10.29

中国象棋残局 中国象棋残局模拟器ChineseChess1 2023.10.29 原来圈粉丝&#xff0c;钓鱼&#xff0c;只要不要脸就OK&#xff01;&#xff01;

洛谷趣题【过河卒】参考题解

背景 今天逛洛谷才注意到这道题&#xff0c;原题连接【P1002 [NOIP2002 普及组] 过河卒 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)】 对于爱下棋的我来说&#xff0c;当然是必刷之题。 题意 小卒起始点在左上角(0,0)处&#xff0c;我们的程序将接收两个坐标&#xff1…

解决历史图片创建时间错误(批量修改文件创建时间)

最近在整理历史文件&#xff0c;发现很多历史图片&#xff0c;截图&#xff0c;微信拍照等途径创建的图片没有创建时间和修改时间&#xff0c;导致在相册时间轴错误。集中出现在整理的当天。 这些图片基本在文件名都含有创建时间&#xff0c;大多格式如下&#xff1a; 对于其中…

集成Swagger

基于knife4j集成swagger 集成的步骤 1添加依赖 2配置包扫描 3给每个Controller配置API 4给每个方法&#xff08;接口&#xff09;配置入参、返回值的说明 5针对入参的实体、返回类型VO做配置 添加依赖 <dependency><groupId>com.github.xiaoymin</groupId…

web前端JS基础-----制作进度条

1&#xff0c;参考代码 <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title></head><body><progress id"pro" max"100" value"0"></progress><scrip…

hive 函数使用详解

一、前言 在任何一种编程语言中,函数可以说是必不可少的,像mysql、oracle中,提供了很多内置函数,或者通过自定义函数的方式进行定制化使用,而hive作为一门数据分析软件,随着版本的不断更新迭代,也陆续出现了很多函数,以满足日常数据查询与分析的各种场景。 二、hive 函…

阻塞队列.

目录 ♫什么是阻塞队列 ♫什么是生产-消费者模式 ♫实现一个阻塞队列 ♫BlockingQueue ♫什么是阻塞队列 阻塞队列是一种特殊的队列&#xff0c;它除了具备队列的先进先出的特点外&#xff0c;还具有以下特点&#xff1a; ♩.如果队列为空时&#xff0c;执行出队列操作&#xf…

设计模式:责任链模式(C#、JAVA、JavaScript、C++、Python、Go、PHP)

上一篇《享元模式》 下一篇《解释器模式》 简介&#xff1a; 责任链模式&#xff0c;它是一种行为型设计模式&#xff0c;它将许多对象连接起来形成一条链&#xff0c;每个对象处理不同的请求&#xff0c…

腾讯云轻量应用服务器地域怎么选择比较好?

腾讯云轻量应用服务器地域怎么选比较好?腾讯云轻量应用服务器地域是指轻量服务器数据中心所在的地理位置&#xff0c;如上海、广州和北京等地域&#xff0c;如何选择地域&#xff1f;腾讯云百科txybk.com关于地域的选择建议就近原则&#xff0c;用户距离轻量服务器地域越近&am…

物联网AI MicroPython传感器学习 之 mpu6050六轴陀螺仪传感器

学物联网&#xff0c;来万物简单IoT物联网&#xff01;&#xff01; 一、产品简介 MPU6050是一款6轴运动传感器&#xff0c;它集成了3 轴MEMS 陀螺仪&#xff0c;3 轴MEMS加速度计&#xff0c;以及一个可扩展的数字运动处理器DMP&#xff08;Digital Motion Processor&#xf…

“破解我!“---160个CrackMe练习001-Acid buen.exe

文章目录 前言题目分析破解过程Serial/Name验证方式爆破注册机 追码 Serial验证 前言 想开个系列&#xff0c;160个Crackme的练习&#xff0c;这个在52pojie上有个精华帖总结&#xff0c;写的特别好&#xff0c;推荐&#xff01;写这个系列主要还是记录一下自己的学习记录&…

Apollo模块:开源配置管理的明日之星

Apollo模块 概述目录结构功能模块编译福利活动 主页传送门&#xff1a;&#x1f4c0; 传送 概述 目录结构 按照功能模块划分&#xff1a; |-cyber 消息中间件&#xff0c;替换ros作为消息层 |-docker 容器相关 |-docs 文档相关 |-modules 自动驾驶模块&#xff0c;主要的定位…

Android 主题 vs 样式

关于作者&#xff1a;CSDN内容合伙人、技术专家&#xff0c; 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 &#xff0c;擅长java后端、移动开发、商业变现、人工智能等&#xff0c;希望大家多多支持。 目录 一、导读二、概览三、相关知识3.1 theme&#xff01; st…

YUV的红蓝颠倒(反色)的原因及解决

原因 UV排列反了。 比如说&#xff0c;NV21和YUV420SP的Y排列相同&#xff0c;UV则相反。给你YUV420SP&#xff0c;你当作NV21保存JPG&#xff0c;就会发生红蓝拿起。 解决办法 就是把UV互换一下。具体代码&#xff1a; NV21转YUV420SP的代码_nv21转yuv420格式-CSDN博客 …

[毕设记录]@开题调研:一些产品

我感觉产品能代表落地的一些实际应用&#xff0c;会和研究的角度有些差别&#xff0c;但是需求和兴趣往往是从现实中来的&#xff0c;在上一篇blog里面看外国blog的时候顺着搜搜到了很多国外的智慧校园chatbot解决方案 文章目录 Comm100streebomodern campusUniBuddy Comm100 …

git建仓库小记

git建仓库小记 1.新建远端git仓库2.新建本地仓库3.添加ssh key4.将本地仓库关联到远端5.push & pull 每次新建git项目的时候都要翻翻之前收藏的几篇帖子&#xff0c;索性自己汇总一下记录&#xff0c;以后一次粘贴搞定。 1.新建远端git仓库 这个比较简单&#xff0c;网页…

Rust编程基础之变量与可变性

1.Rust变量 在Rust语言中, 变量默认是不可改变的(immutable), 这是Rust提供给我们的众多优势之一, 让我们可以充分利用Rust提供的安全性和简单并发性来编写代码。 当变量不可变时, 一旦值被绑定在一个名称上, 就不能改变这个值。下面是一段代码的例子: fn main() {let x 1;…

vue3-访问本地json文件

将json文件放在public文件夹中 用fetch可以直接访问public下的文件 fetch(/tab-3-data/costOverhaul.json).then(response > response.json()).then(res > {console.log(res)//数据});