C学习--内存管理

#灵感#

当计算机执行一个程序时,必须有一种方法来存储程序本身和运算所得的数据。

总的来讲,计算机硬件中任何能够存储和检索信息的部分都是存储设备。当前运行的程序存放的存储器称为主存储器(primary storage),常常称 内存(memory)。自从1964年冯•诺依曼首次提出这个构思以来,计算机一直使用同一个存储器来存放组成程序的指令和计算过程中用到的数据。
人们将内存系统设计得十分高效,以使CPU能够以极快的速度获取它需要的信息。在如今的计算机中,内存通常构建在一个特殊的集成电路芯片上,这个集成电路芯片称作RAM,即随机访问存储器,它使程序能够在任何时刻访问任一存储单元。

~~~~~~~~~~----------------------··············分割线··············~~~~~~~~~~~~~~~~~~~~~~~~~~~

目录

1.内存区域划分

2.动态内存分配

示例代码:

3.栈与堆的区别

4.常见问题

引申

malloc

动态数组

memset

char **p


以下内容是AI 助手总结的,可能不对,请谨慎辨别。

C语言中的内存管理是编程的重要部分,它涉及到程序运行时如何分配、使用和释放内存。以下是C语言内存管理的核心概念和相关内容:


1.内存区域划分

在C语言中,程序运行时的内存通常被划分为以下 4 个区域:

  • 可执行代码区(Text Segment):
    • 存储程序的机器代码。
    • 这部分内存是只读的,防止程序意外修改自身的代码。
  • 全局/静态数据区(Global/Static Data Segment):
    • 存储全局变量和静态变量。
    • 初始化的全局变量和静态变量存储在已初始化数据区。
    • 未初始化的全局变量和静态变量存储在BSS段(Block Started by Symbol)
    • 对于全局变量或使用static关键字修饰的变量,系统会在程序启动时为其分配内存,并且这些内存会一直保留到程序结束。

--------------可执行代码和静态数据,存储 在固定的内存位置。

  • 堆区(Heap Segment):(程序可用的,未分配的内存资源称为堆)
    • 动态分配的内存区域,由程序员通过malloc、calloc、realloc等函数申请。
    • 使用完毕后需要通过free函数释放。
  • 栈区(Stack Segment):
    • 自动分配和释放的内存区域,用于存储函数调用时的局部变量、函数参数和返回地址。
    • 栈的分配和释放由编译器自动完成。调用函数时给变量分配空间,函数返回时释放。
  • 常量区(Constant Data Segment):
    • 存储常量数据,如字符串字面量和const修饰的变量。
    • 这部分内存通常是只读的。字符串常量存储在只读数据段或常量区,系统会自动分配内存并管理其生命周期。

经过上述讲解,需要程序员手动管理的只有堆区,而常量、全局变量、静态变量、字符串、函数的局部变量都是系统自动分配的。


2.动态内存分配

动态内存分配是指在程序运行时根据需要申请内存,主要通过以下函数实现:

  • malloc(size_t size):
    • 分配指定大小的内存块,返回指向该内存块起始位置的指针。
    • 返回的内存未初始化。
  • calloc(size_t num, size_t size):
    • 分配num * size大小的内存块,并将其初始化为0。
  • realloc(void *ptr, size_t size):
    • 调整已分配内存块的大小,可以增大或缩小。
  • free(void *ptr):
    • 释放之前通过malloc、calloc或realloc分配的内存。
示例代码:
#include <stdio.h>
#include <stdlib.h>int main() {int *p = (int*)malloc(5 * sizeof(int)); // 分配5个int大小的内存if (p == NULL) {printf("Memory allocation failed\n");return 1;}for (int i = 0; i < 5; i++) {p[i] = i + 1;}for (int i = 0; i < 5; i++) {printf("%d ", p[i]);}printf("\n");free(p); // 释放内存return 0;
}

3.栈与堆的区别

特性栈(Stack)堆(Heap)
分配方式编译器自动分配和释放程序员手动分配和释放
分配速度较慢
生命周期函数调用结束时自动销毁需要手动释放
大小限制较小(受系统限制)较大(受系统内存限制)

4.常见问题

  • 内存泄漏:
    • 动态分配的内存未被释放,导致程序占用的内存不断增加。
    • 解决方法:确保每次malloc或calloc都有对应的free。
  • 野指针:
    • 指向已释放内存或未分配内存的指针。
    • 解决方法:将释放后的指针置为NULL。
  • 越界访问:
    • 访问数组或指针超出其分配范围。
    • 解决方法:确保访问索引在合法范围内。

引申

malloc

例句:p_child = (struct diag_child *)malloc(sizeof(struct diag_child));

malloc函数返回类型为void * 指针值待分配的内存以字节为单位。原型为:

void *malloc(int nBytes);  // * 表示结果是和函数名而非基本类型相联系的指针。

ANSI C能在指向void的指针类型和指向基本类型的指针类型间自动进行转换。例如,如果声明字符指针cp为:
char *cp;
就可以用语句
cp = malloc(10);// 1   10个字节空间
将malloc的结果直接赋给cp。许多程序员在赋值之前,会使用下列强制类型转换将malloc返回的结果转换成指向字符的指针。这样做的一部分原因是因为历史因素,另一部分原因是这样做能使指针类型间的转换更清楚。
cp = (char *) malloc (10) ; // 2
不管有没有明确使用强制类型转换,这条语句均可分配10个字节的新内存空间,并将第一个字节的地址存放在cp中。

实例:

调用malloc时要检查

由于计算机内存系统的大小是有限的,堆的空间终会用完。此时malloc返回指针NULL表示分配所需内存块的工作失败。谨慎的程序员应该在每次调用malloc时都检查失败的可能性。所以,分配一个动态数组后,需要写如下语句:
arr = malloc(10 * sizeof (int)) ;
if (arr == NULL) Error ("No memory available");

因为动态分配往往会频繁用于多个程序,所以检查错误是必要的。(堆内存的大小取决于操作系统、硬件限制以及程序的具体实现)

使用完后释放内存

例句:

free(arr);

arr = NULL;//将p置为NULL,防止越界访问

保证不会发生内存不够的一种方法是一旦使用完已分配的空间就立刻释放它。标准ANSI库提供了函数free,用于归还以前由malloc分配出去的堆内存。例如,如果你能肯定已经不再使用分配给arr的内存,就可以通过调用
free(arr);
释放该空间。

/*但事实证明,知道何时释放一块内存并不那么容易。根本问题在于分配和释放内存的操作分别属于接口两边的实现及客户。实现知道何时该分配内存,返回指针给客户,但它并不知道何时客户结束使用已分配的对象,所以释放内存是客户的责任,虽然客户可能并不十分了解对象的结构。
对现在的大多数计算机的内存来说,你可以随意分配所需内存而不需要考虑释放内存的问题。这种策略几乎对所有运行时间不长的程序还是有效的。内存有限的问题只有在设计一个需要运行很长时间的应用,比如所有其他系统所依靠的操作系统时,才变得有意义。在这些应用中,不需要内存时释放它们是很重要的。
某些语言支持那些能主动检查正在使用的内存,并释放不再使用的内存的动态分配系统。此策略称为碎片收集(garbage collection)。C语言中也有碎片收集分配器,而且它未来的应用也许会更广泛。如果是这样的话,即使在长时间运行的程序中,也可以忽略释放内存的问题,因为你可以依靠碎片收集器自动执行释放内存的操作。*/
 

动态数组

例句:

int *arr;
arr = malloc(10 * sizeof (int));

分配在堆上并用指针变量引用的数组称为动态数组(dynamic array),它在现代程序设计中有非常重要的作用。一般来说,分配一个动态数组包含以下步骤:
1)声明一个指针变量,用以保存数组基地址。
2)调用malloc函数为数组中的元素分配内存。由于不同的数据类型要求不同大小的内存空间,所以ma11oc调用必须分配的字节大小= 数组元素数* 每个元素字节大小 的内存空间。
3) 将malloc的结果赋给指针变量。
比如,要给一个含10个元素的整型数组分配空间,然后将该内存赋给变量arr,你必须先用
int *arr;
声明arr,随后使用
arr = malloc(10 * sizeof (int));
来分配空间。

memset

例句: memset(p_child, 0, sizeof(*p_child));

1、memset的作用是将某一块内存中的内容 全部设置为指定的值。

void *dest为内存的地址,int c是赋予的值,size_t count是需要赋值的字节长度;

#include <memory.h>
#include <stdio.h>void main(){char string1[]="hello,hello";printf("1 is %s\n",string1);memset(&string1,'*',6);printf("2 is %s\n",string1);}

输出结果:

2、memset 通常用于为malloc新申请的内存做初始化工作。(作用的对象是一块内存)

#include <memory.h>
#include <stdio.h>typedef struct diag_child {char name[40];}child;void main(){struct diag_child *p_child;p_child = (struct diag_child *)malloc(sizeof(struct diag_child));printf("size is %d \n",sizeof(struct diag_child));if (p_child == NULL) {printf(" malloc fail!\n");return NULL;}memset(p_child, 0, sizeof(*p_child));for(int i=0;i<40;i++) printf("%d",p_child->name[i]);printf("\nchar is %d \n",p_child->name[6]);}

结果:

char **p

定义一个指针变量p, 指向另外一个指针变量,该指针变量又指向一个基本字符型变量。指针运算符是从右向左的,所以char **p, 等价于 char* (*p);

char* 是一个指向字符(char)的指针,通常用于表示字符串。

char** 则是一个指向 char* 的指针,这意味着它可以指向一个 char* 类型的变量。这种类型的指针通常用于处理字符串数组或动态分配的字符串列表。

例如:

char *str1 = "Hello";
char *str2 = "World";
char **p;p = &str1; // p 现在指向 str1
printf("%s\n", *p); // 输出: Hellop = &str2; // p 现在指向 str2
printf("%s\n", *p); // 输出: World

例子中,p 被用来存储 str1 和 str2 的地址,这两个变量本身是字符串(字符数组)的指针。

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

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

相关文章

使用 Docker Compose 安装 PostgreSQL 16

前面是指南&#xff0c;后面是实际工作日志。 1. 创建 docker-compose.yml 文件 yaml 复制 下载 version: 3.9 services:postgres:image: postgres:16container_name: postgres-16environment:POSTGRES_USER: your_username # 替换为你的用户名POSTGRES_PASSWORD: your…

从数据报表到决策大脑:AI重构电商决策链条

在传统电商运营中&#xff0c;决策链条往往止步于“数据报表层”&#xff1a;BI工具整合历史数据&#xff0c;生成滞后一周甚至更久的销售分析&#xff0c;运营团队凭经验预判需求。当爆款突然断货、促销库存积压时&#xff0c;企业才惊觉标准化BI的决策时差正成为增长瓶颈。 一…

SpringBoot 自动化部署实战:CI/CD 整合方案与避坑指南

引言 在微服务架构盛行的今天&#xff0c;SpringBoot 凭借其开箱即用的特性成为 Java 后端开发的主流框架。然而&#xff0c;随着项目规模扩大&#xff0c;手动部署的效率瓶颈逐渐显现。本文将结合 GitLab CI/CD、Jenkins 等工具&#xff0c;深入探讨 SpringBoot 项目的自动化部…

力扣HOT100之二分查找:35. 搜索插入位置

这道题属于是二分查找的入门题了&#xff0c;我依稀记得一些二分查找的编码要点&#xff0c;但是最后还是写出了一个死循环&#xff0c;无语(ˉ▽ˉ&#xff1b;)…又回去看了下自己当时的博客和卡哥的视频&#xff0c;这才发现自己分情况只分了两种&#xff0c;最后导致死循环…

VS创建Qt项目,Qt的关键字显示红色波浪线解决方法

如图所示&#xff0c;VS2017新创建的Qt项目&#xff0c;编译正常&#xff0c;关键字显示识别失败&#xff0c;显示红色波浪线&#xff0c;编译运行没问题。 解决方法&#xff1a; 如下图所示&#xff0c;C/C -> 常规 -> 附加包含目录 ->添加Qt的Include路径 如下图…

pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)

目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关&#xff0…

k8s从入门到放弃之HPA控制器

k8s从入门到放弃之HPA控制器 Kubernetes中的Horizontal Pod Autoscaler (HPA)控制器是一种用于自动扩展部署、副本集或复制控制器中Pod数量的机制。它可以根据观察到的CPU利用率&#xff08;或其他自定义指标&#xff09;来调整这些对象的规模&#xff0c;从而帮助应用程序在负…

人机融合智能 | “人智交互”跨学科新领域

本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…

ccf中学生计算机程序设计入门篇课后题p164页test(1)-2 输入一个数,统计这个数二进制中1的个数

include <iostream> using namespace std;int main() {int x;int n 0;// 输入数据cin >> x;// 统计x二进制中1的个数for (n 0; x ! 0; x & x - 1) {n;}// 输出结果cout << n << endl;return 0; }程序解释&#xff1a; 输入&#xff1a;程序从标…

无人机侦测与反制技术的进展与应用

国家电网无人机侦测与反制技术的进展与应用 引言 随着无人机&#xff08;无人驾驶飞行器&#xff0c;UAV&#xff09;技术的快速发展&#xff0c;其在商业、娱乐和军事领域的广泛应用带来了新的安全挑战。特别是对于关键基础设施如电力系统&#xff0c;无人机的“黑飞”&…

【Go语言基础【18】】Map基础

文章目录 零、概述一、Map基础1、Map的基本概念与特性2、Map的声明与初始化3、Map的基本操作 二、Map的底层实现三、Map的注意事项 零、概述 Map与其他语言的对比 特性Go mapJava HashMapPython dict并发安全非线程安全&#xff0c;需手动加锁非线程安全&#xff08;Concurre…

Qt客户端技巧 -- 窗口美化 -- 窗口阴影

不解析&#xff0c;直接给示例 窗口设为不边框且背景透明,好用来承载阴影 窗口一个Widget用来作真实窗口的作用&#xff0c;在真实窗口上加上阴影特效 上下两层Widget方式 main.cpp #include <QtCore/qglobal.h> #if QT_VERSION > 0x050000 #include <QtWidget…

优选算法第十二讲:队列 + 宽搜 优先级队列

优选算法第十二讲&#xff1a;队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…

华为OD最新机试真题-流水线-OD统一考试(B卷)

题目描述: 有个工厂有m条 流水线,来并行完成n个独立的作业,该工厂设置了一个调度系统,在安排作业时,总是优先执行处理时间最短的作业。 现给定流水线个数m,需要完成的作业数n,每个作业的处理时间分别为t1,.2..n。请你编程计算处理完所有作业的耗时为多少? 当n>m时

区块链技术概述

区块链技术是一种去中心化、分布式账本技术&#xff0c;通过密码学、共识机制和智能合约等核心组件&#xff0c;实现数据不可篡改、透明可追溯的系统。 一、核心技术 1. 去中心化 特点&#xff1a;数据存储在网络中的多个节点&#xff08;计算机&#xff09;&#xff0c;而非…

项目css / js的兼容性next项目实践处理

之前写过一篇&#xff0c;但是没有css的处理&#xff0c;但是那一篇有几个文章蛮好的https://blog.csdn.net/SaRAku/article/details/144704916 css兼容性和js兼容性 1. 确定需要兼容的版本 先确定你们的兼容性版本&#xff0c;我们的兼容性以APP H5的兼容版本为最低兼容性&…

Vue3 + Vite 中使用 Lodash-es 的防抖 debounce 详解

Vue3 Vite 中使用 Lodash-es 的防抖(debounce)详解 在 Vue3 Vite 项目中&#xff0c;debounce 是 lodash-es 中最常用的功能之一&#xff0c;它可以帮助我们优化高频事件的处理。下面我将详细讲解 debounce 的使用方法&#xff0c;并提供一个完整的示例。 Debounce 核心概念…

MySQL--慢查询日志、日志分析工具mysqldumpslow

mysqldumpslow 常用参数&#xff1a; -s&#xff0c;是order的顺序----- al 平均锁定时间-----ar 平均返回记录时间-----at 平均查询时间&#xff08;默认&#xff09;-----c 计数-----l 锁定时间-----r 返回记录-----t 查询时间-t&#xff0c;是top n的意思&#xff0c;即为返…

C++课设:实现图书馆借阅记录系统(支持书籍管理、借阅功能、超期检测提醒)

名人说&#xff1a;路漫漫其修远兮&#xff0c;吾将上下而求索。—— 屈原《离骚》 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 专栏介绍&#xff1a;《编程项目实战》 目录 一、系统概述与设计思路1. 系统核心功能…

矩阵和向量范数的区别分析

文章目录 1. 研究对象本质差异2. 运算和作用方式不同3. 应用需求不同4. 数学性质和理论体系不同5. 几何直观不同6. 范数定义区别7. 范数计算方式区别8. 范数几何意义区别9. 范数相容性区别总结 1. 研究对象本质差异 向量本质&#xff1a;向量是具有大小和方向的一维有序数组&a…