C语言中如何进行内存管理

news2024/11/19 3:31:28

主页:17_Kevin-CSDN博客

收录专栏:《C语言》

C语言是一种强大而灵活的编程语言,但与其他高级语言不同,它要求程序员自己负责内存的管理。正确的内存管理对于程序的性能和稳定性至关重要。


一、引言

C 语言是一门广泛使用的编程语言,它为程序员提供了对内存的直接控制能力。这种对内存的控制使得 C 语言非常灵活,但也带来了更大的责任。在 C 语言中,程序员需要负责内存的分配和释放,否则可能会导致内存泄漏和其他内存管理问题。本文将深入探讨 C 语言的内存管理机制,包括内存分配、内存释放、内存泄漏等问题。

二、内存分配

C语言中有三种内存分配方式:

  • 静态内存分配
  • 栈内存分配
  • 动态内存分配

静态内存分配

静态内存分配:静态内存分配是在程序编译时进行的,它将内存分配给全局变量和静态变量。全局变量和静态变量的内存空间在程序运行期间一直存在,直到程序结束。静态内存分配的优点是内存分配和释放的效率高,缺点是内存使用不灵活,无法根据需要动态调整内存大小。

当我们创建变量的时候,系统将会自动的为变量分配空间:

//创建变量a
int a = 0;

可以观察到当创建变量a后系统会为a分配一块内存,这就是静态内存的分配。

栈内存的分配

栈内存分配是在程序运行时进行的,它将内存分配给函数内部的局部变量。栈内存的空间是有限的,当函数执行完毕后,栈内存会自动释放。栈内存分配的优点是内存管理简单,缺点是内存空间有限,不适合分配大内存。

例如:

当创建一个函数,在函数中创建一个变量local_num,和local_name

void test()
{
	int local_num = 17;
	char local_name = "Kevin";

}

int main()
{
	test();

	return 0;
}

 当调试时走过这两个局部变量但没有走出函数时可以观察到这两个变量成功的创建了:

 当走出函数后刷新即可发现两个变量变成了未标识变量:

这就是在函数中的栈内存分配,随用随分配,在用过后就销毁。

动态内存分配

动态内存分配是在程序运行时根据需要进行的内存分配。它使用 malloc() 函数或 calloc() 函数来分配内存空间,使用 free() 函数来释放内存空间。动态内存分配的优点是内存使用灵活,可以根据需要动态调整内存大小,缺点是内存管理复杂,容易出现内存泄漏等问题。

具体函数的使用可以翻阅博主这篇博客进行查阅

C/C++ (stdio.h)标准库详解-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/SDFsoul/article/details/135568683

1. malloc

malloc() 函数用于分配指定大小的内存块

int main() 
{
    int* ptr;

    // 分配 10 个整数的内存块
    ptr = (int*)malloc(10 * sizeof(int));

    if (ptr == NULL) 
    {
        printf("内存分配失败\n");
        exit(1);
    }

    // 访问分配的内存
    for (int i = 0; i < 10; i++) 
    {
        ptr[i] = i + 1;
    }

    // 输出分配的内存中的值
    for (int i = 0; i < 10; i++) 
    {
        printf("%d ", ptr[i]);
    }
    printf("\n");

    // 释放内存
    free(ptr);

    return 0;
}

在上述示例中,malloc() 函数用于分配 10 个整数的内存块。如果内存分配成功,ptr 将指向分配的内存块,否则输出错误信息并退出程序。然后,可以通过 ptr 访问和修改分配的内存。最后,使用 free() 函数释放分配的内存块。

2. calloc

calloc() 函数用于分配指定数量的元素,并将它们初始化为 0

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

int main() 
{
    int* ptr;

    // 分配 10 个整数的内存块,并将它们初始化为 0
    ptr = (int*)calloc(10, sizeof(int));

    if (ptr == NULL) 
    {
        printf("内存分配失败\n");
        exit(1);
    }

    // 访问分配的内存
    for (int i = 0; i < 10; i++) 
    {
        printf("%d ", ptr[i]);
    }
    printf("\n");

    // 释放内存
    free(ptr);

    return 0;
}

在上述示例中,calloc() 函数用于分配 10 个整数的内存块,并将它们初始化为 0。如果内存分配成功,ptr 将指向分配的内存块,否则输出错误信息并退出程序。然后,可以通过 ptr 访问和修改分配的内存。最后,使用 free() 函数释放分配的内存块。

3. free

free() 函数用于释放之前分配的内存块,在上文malloc和calloc中均用到了free函数,目的就是在使用完分配的内存块后进行释放,避免内存泄漏。

三、内存释放

在 C 语言中,内存释放是非常重要的。如果忘记释放不再使用的内存,就会导致内存泄漏。内存泄漏会导致程序的性能下降,甚至可能导致程序崩溃。在 C 语言中,有两种常见的内存释放方式:手动释放和自动释放。

1. 手动释放

手动释放是指程序员使用 free() 函数来释放不再使用的内存空间。在使用动态内存分配时,程序员需要在不再使用内存空间时手动调用 free() 函数来释放内存。

#include <stdlib.h>

int* allocate_memory(int size) 
{
    int* memory = (int*)malloc(size * sizeof(int));
    if (memory == NULL) 
    {
        printf("内存分配失败\n");
        exit(1);
    }
    return memory;
}

void free_memory(int* memory) 
{
    if (memory != NULL) 
    {
        free(memory);
    }
}

int main() 
{
    int* dynamic_memory = allocate_memory(10);
    for (int i = 0; i < 10; i++) 
    {
        dynamic_memory[i] = i + 1;
    }

    for (int i = 0; i < 10; i++) 
    {
        printf("%d ", dynamic_memory[i]);
    }
    printf("\n");

    free_memory(dynamic_memory);
    return 0;
}
  1. allocate_memory 函数用于动态分配一块大小为 size 个整数的内存空间,并返回指向该内存空间的指针。如果内存分配失败,程序会输出提示信息并调用 exit(1) 来退出程序。

  2. free_memory 函数用于释放动态分配的内存空间,首先检查指针是否为空,然后调用 free 函数进行内存释放。

  3. main 函数中,首先调用 allocate_memory 函数分配了包含 10 个整数的内存空间,并将返回的指针赋值给 dynamic_memory。然后使用循环给动态分配的内存赋值,并输出每个元素的值。

  4. 最后,通过调用 free_memory 函数释放动态分配的内存空间,避免内存泄漏。

2. 自动释放

自动释放是指在某些情况下,C 语言的编译器会自动释放不再使用的内存空间。例如,当函数执行完毕后,编译器会自动释放函数内部的栈内存。

#include <stdio.h>

void function() 
{
    int local_variable = 30;
    printf("局部变量的值:%d\n", local_variable);
}

int main() 
{
    function();
    return 0;
}

四、内存泄漏

内存泄漏指程序在不再需要使用内存时未将其释放,导致系统内存资源浪费。内存溢出则是指程序访问超出了已分配内存块的范围(例如数组越界访问)。


五、指针和内存

在C语言中,指针与内存密切相关。我们可以通过指针指向目标地址来直接操作内存,包括访问、修改和释放内存。

指针数组

int *ptrArray[10]; // 声明一个包含10个整型指针的数组

二级指针

int **pptr; // 声明一个二级整型指针
int value = 100;
int *ptr = &value;
pptr = &ptr; // 将ptr的地址赋给pptr

指针与结构体

结构体和指针的结合也是C语言中常见的用法,可以方便地操作复杂的数据结构(例如链表)。


希望这能帮助您更好地理解和应用C语言中的内存管理知识。

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

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

相关文章

【算法历练】动态规划副本—路径问题

&#x1f3ac;慕斯主页&#xff1a;修仙—别有洞天 ♈️今日夜电波&#xff1a;宙でおやすみ 1:02━━━━━━️&#x1f49f;──────── 2:45 &#x1f504; ◀️ ⏸ ▶️ ☰ &#…

现在在市场上云主机一般多少钱?影响其价格的因素有哪些

现在很多人都会购买云主机来帮助自己存储一些数据&#xff0c;但是很多人在购买云主机的时候最担心的就是云主机的价格。 由于很多人担心云服务器的价格会很高&#xff0c;因此一直在密切关注目前市场上各品牌云主机的相关价格。 下面就给大家详细介绍一下现在市场上一台云主机…

【DDD】学习笔记-领域驱动设计对持久化的影响

资源库的实现 如何重用资源库的实现&#xff0c;以及如何隔离领域层与基础设施层的持久化实现机制&#xff0c;具体的实现还要取决于开发者对 ORM 框架的选择。Hibernate、MyBatis、jOOQ 或者 Spring Data JPA&#xff08;当然也包括基于 .NET 的 Entity Framework、NHibernat…

若依Vue3:新一代前后端分离权限管理系统

若依Vue3&#xff1a;新一代前后端分离权限管理系统 随着技术的不断进步&#xff0c;前后端分离的开发模式逐渐成为主流&#xff0c;特别是在构建权限管理系统时。在这样的背景下&#xff0c;若依Vue3应运而生&#xff0c;作为基于Spring Boot、Spring Security、JWT、Vue3、V…

【C++】树形关联式容器set、multiset、map和multimap的介绍与使用

&#x1f440;樊梓慕&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C》《Linux》《算法》 &#x1f31d;每一个不曾起舞的日子&#xff0c;都是对生命的辜负 目录 前言 1.关联式容器 2.键…

二叉搜索树在线OJ题讲解

二叉树创建字符串 我们首先进行题目的解读&#xff1a; 大概意思就是用&#xff08;&#xff09;把每个节点的值给括起来&#xff0c;然后再经过一系列的省略的来得到最后的结果 大家仔细观察题目给出的列子就可以发现&#xff0c;其实这个题目可以大致分为三种情况&#xff1…

基于 LVGL 使用 SquareLine Studio 快速设计 UI 界面

目录 简介注册与软件获取工程配置设计 UI导出源码板级验证更多内容 简介 SquareLine Studio 是一款专业的 UI 设计软件&#xff0c;它与 LVGL&#xff08;Light and Versatile Graphics Library&#xff0c;轻量级通用图形库&#xff09;紧密集成。LVGL 是一个轻量化的、开源的…

[linux][xdp] xdp 入门

xdp 全称 eXpress Data Path&#xff0c;是 linux ebpf 中的一个功能。ebpf 在内核中预留了一些插入点&#xff0c;用户可以在这些插入点插入自己的处理逻辑&#xff0c;当数据路过插入点时可以做一些预期的处理&#xff0c;具体实现方式如下&#xff1a; ① 用户编写数据处理…

【C++私房菜】序列式容器的迭代器失效问题

目录 一、list的迭代器失效 二、vector的迭代器失效 1、空间缩小操作 2、空间扩大操作 三、总结 在C中&#xff0c;当对容器进行插入或删除操作时&#xff0c;可能会导致迭代器失效的问题。所谓迭代器失效指的是&#xff0c;原先指向容器中某个元素的迭代器&#xff0c;在…

尚硅谷webpack5笔记2

Loader 原理 loader 概念 帮助 webpack 将不同类型的文件转换为 webpack 可识别的模块。 loader 执行顺序 分类pre: 前置 loadernormal: 普通 loaderinline: 内联 loaderpost: 后置 loader执行顺序4 类 loader 的执行优级为:pre > normal > inline > post 。相…

在Node.js中如何实现用户身份验证和授权

当涉及到构建安全的应用程序时&#xff0c;用户身份验证和授权是至关重要的一环。在Node.js中&#xff0c;我们可以利用一些流行的库和技术来实现这些功能&#xff0c;确保我们的应用程序具有所需的安全性。本篇博客将介绍如何在Node.js中实现用户身份验证和授权。 用户身份验…

密码学系列(四)——对称密码2

一、RC4 RC4&#xff08;Rivest Cipher 4&#xff09;是一种对称流密码算法&#xff0c;由Ron Rivest于1987年设计。它以其简单性和高速性而闻名&#xff0c;并广泛应用于网络通信和安全协议中。下面是对RC4的详细介绍&#xff1a; 密钥长度&#xff1a; RC4的密钥长度可变&am…

精品springboot相亲网站预约

《[含文档PPT源码等]精品基于springboot相亲网站[包运行成功]》该项目含有源码、文档、PPT、配套开发软件、软件安装教程、项目发布教程、包运行成功&#xff01; 软件开发环境及开发工具&#xff1a; Java——涉及技术&#xff1a; 前端使用技术&#xff1a;HTML5,CSS3、Ja…

在Golang中简化日志记录:提升性能和调试效率

最大化效率和有效故障排除&#xff1a;在Golang中简化日志记录 日志记录是软件开发的一个基本方面&#xff0c;有助于调试、监控和理解应用程序的流程。在Golang中&#xff0c;有效的日志记录实践可以显著提高性能并简化调试过程。本文探讨了优化Golang日志记录的技术&#xf…

常用git 打tag命令

1.查看所有tag git tag 2.创建 v5.0.0的tag git tag v5.0.0 git tag &#xff08;创建后查看&#xff09; 3.推送到远程tag git push origin v5.0.0 4.删除远程tag git push origin --delete v5.0.0 5.删除本地tag git tag -d v5.0.0 6.添加带有备注信息的tag git tag v5.…

SpringCloud认识微服务

文章目录 1.1.单体架构1.2.分布式架构1.3.微服务1.4.SpringCloud1.5.总结 随着互联网行业的发展&#xff0c;对服务的要求也越来越高&#xff0c;服务架构也从单体架构逐渐演变为现在流行的微服务架构。这些架构之间有怎样的差别呢&#xff1f; 微服务架构是一种架构模式&…

Qcom camera hal简介

目录 背景 Android相机软件框架 Qcom HAL主要包括Camx和Chi两部分 Camx中Pipeline 和 node 下图是简单模型的pipeline (sensor --> IFE --> IPE --> SinkTarget) Pipeline中的buffer管理 IFE output port的buffer在Camx中申请 IPE output port使用的buffer来自…

JavaEE进阶(7)Spring Boot 日志(概述、用途、使用:打印日志,框架介绍,SLF4J 框架介绍、更简单的日志输出)

接上次博客&#xff1a;JavaEE进阶&#xff08;6&#xff09;SpringBoot 配置文件&#xff08;作用、格式、properties配置文件说明、yml配置文件说明、验证码案例&#xff09;-CSDN博客 目录 日志概述 日志的用途 日志使用 打印日志 在程序中获取日志对象 使用日志对象…

Twing模板注入 [BJDCTF2020]Cookie is so stable1

打开题目 我们先抓包分析一下 可以输入{{7*7}}处发包试一下 可以看到在cookie处存在ssti模板注入 输入{{7*‘7’}}&#xff0c;返回49表示是 Twig 模块 输入{{7*‘7’}}&#xff0c;返回7777777表示是 Jinja2 模块 在这里可以看出是Twing模块 我们直接用固定payload就可以…

【STM32】STM32学习笔记-修改主频 睡眠模式 停止模式 待机模式(45)

00. 目录 文章目录 00. 目录01. PWR简介02. 修改主频接线图03. 修改主频相关API04. 修改主频程序示例05. 睡眠模式接线图06. 睡眠模式相关API07. 睡眠模式程序示例08. 停止模式接线图09. 停止模式相关API10. 停止模式程序示例11. 待机模式接线图12. 待机模式相关API13. 待机模式…