C++奇迹之旅:C++内存管理的机制初篇

news2024/12/23 16:48:58

请添加图片描述

文章目录

  • 📝C/C++内存分布
  • 🌠 C语言中动态内存管理方式
    • 🌉C++内存管理方式
  • 🌠new/delete操作内置类型
    • 🌉C与C++链表构建对比
  • 🚩总结


📝C/C++内存分布

这是C/C++中程序内存区域划分图:
在这里插入图片描述
数据段:也叫静态数据段或初始化数据段,用于存储程序中的全局变量和静态变量,这些变量在程序启动时就已经分配好内存空间并初始化。
代码段:也叫文本段或指令段,用于存储程序的可执行指令代码。
这部分内存区域通常是只读的,程序在运行时不能修改代码段中的内容。

我们先来看下面的一段代码和相关问题

int globalVar = 1;
static int staticGlobalVar = 1;
void Test()
{
	static int staticVar = 1;
	int localVar = 1;
	int num1[10] = { 1, 2, 3, 4 };
	char char2[] = "abcd";
	const char* pChar3 = "abcd";
	int* ptr1 = (int*)malloc(sizeof(int) * 4);
	int* ptr2 = (int*)calloc(4, sizeof(int));
	int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);
	free(ptr1);
	free(ptr3);
}
  1. 选择题:
    选项: A.栈 B.堆 C.数据段(静态区) D.代码段(常量区)
    globalVar在哪里?C staticGlobalVar在哪里?C
    staticVar在哪里?C localVar在哪里?A
    num1 在哪里?A

char2在哪里?A *char2在哪里?_A
pChar3在哪里?A *pChar3在哪里?D
ptr1在哪里?A *ptr1在哪里?B

  1. 全局变量 globalVarstaticGlobalVar 都存储在数据段(静态区)中。全局变量globalVar 的生命周期贯穿整个程序的执行过程,直到程序结束,静态全局变量 staticGlobalVar 的作用域仅限于当前源文件,其生命周期也贯穿整个程序的执行过程。

  2. staticVar 是静态局部变量,也存储在数据段(静态区)中。

  3. localVar 是普通的局部变量,存储在栈中,栈是一种后进先出(LIFO)的数据结构,用于存储函数调用时的局部变量和返回地址等信息,当函数调用结束时,栈中分配给该函数的内存空间会被自动释放。

  4. 局部数组 num1 存储在栈中,数组在内存中是连续分布的,因此 num1 占用了一块连续的栈空间。

  5. *char2char2 在栈中,
    *char2char2[] 是一个局部字符数组,存储在栈上。当你使用字符串字面量初始化它时,编译器会在栈上分配足够的内存空间,并将字符串字面量的内容(包括结尾的 \0)复制到这块内存中,所以 *char2 指向的是存储在栈上的可修改的字符数组。
    *pChar3const char* pChar3 = "abcd"; 中的字符串字面量 "abcd" 存储在只读的数据段(常量区)中。而pChar3 本身是一个指针变量,存储在栈上,它指向常量区中的字符串。由于字符串字面量是只读的,所以通过 *pChar3 我们只能读取字符串的内容,而不能修改它。
    在这里插入图片描述

  6. *pChar3 在栈中, pChar3 在代码段(常量区),指针变量 pChar3 存储在栈中,*pChar3 指向一个字符串常量,该字符串常量存储在代码段(常量区)中,代码段(常量区)用于存储程序中的常量数据,如字符串常量、枚举常量等。这些常量在程序执行期间不会被修改。

  7. ptr1 是局部指针变量,存储在栈上

  8. *ptr1 指向的内容,就是malloc分配的内存,该内存在堆上

总结:

  1. 栈(Stack): 用于存储函数调用时的上下文信息,如返回地址、函数参数和局部变量,遵循先进后出(LIFO)的原则,大小有限,如果使用不当可能导致栈溢出
  2. 堆(Heap): 用于动态分配内存,存储动态分配的对象和数据结构,开发者需要手动管理堆上的内存,分配和释放,大小一般比栈要大得多,但访问速度相对较慢
  3. 数据段(Data Segment): 分为初始化的数据段(.data)和未初始化的数据段(.bss)用于存储全局变量和静态变量,这些变量的生命周期贯穿整个程序执行期
  4. 代码段(Code Segment): 存储可执行的机器指令,通常是只读的,以保护程序代码不被意外修改,存着可执行的代码/只读常量。

填空题:
sizeof(num1) = ____;
sizeof(char2) = ____; strlen(char2) = ____;
sizeof(pChar3) = ____; strlen(pChar3) = ____;
sizeof(ptr1) = ____;

  1. sizeof(num1) = 40;
    num1 是一个包含 10 个 int 类型元素的数组,每个 int 类型占 4 个字节,所以数组大小为 10 * 4 = 40 字节。
  2. sizeof(char2) = 5; strlen(char2) = 4;
    • char2 是一个包含 5 个字符(包括结尾的 '\0')的字符数组,所以 sizeof(char2) 为 5 字节。
    • strlen(char2) 返回字符串的长度,不包括结尾的 '\0',所以为 4。
  3. sizeof(pChar3) = 8; strlen(pChar3) = 4;
    • pChar3 是一个指向字符串常量 "abcd" 的指针,在 32 位系统上,指针大小为 4 字节。在 64 位系统上,指针大小为 8 字节。
    • strlen(pChar3) 返回字符串的长度,不包括结尾的 '\0',所以为 4。
  4. sizeof(ptr1) = 8;
    • ptr1 是一个指向动态分配的 int 类型数组的指针,在 32 位系统上,指针大小为 4 字节。在 64 位系统上,指针大小为 8 字节。

sizeof 和 strlen 区别?
sizeofstrlen 是两个不同的操作符/函数,sizeof 是一个编译时操作,返回变量或数据类型的大小;而 strlen 是一个运行时函数,返回字符串的长度。

  1. sizeof: sizeof 是一个操作符,用于获取变量或数据类型的大小(以字节为单位),它在编译时就确定了返回值,不需要在运行时计算,对于数组,sizeof 返回整个数组的大小,而不是单个元素的大小,对于指针,sizeof 返回指针本身的大小,而不是它所指向的对象的大小。

示例:

char str[] = "hello";
printf("Size of str: %zu\n", sizeof(str)); // 输出: 6 (包括'\0')
printf("Size of char: %zu\n", sizeof(char)); // 输出: 1
  1. strlen:strlen 是一个函数,用于计算字符串的长度(不包括结尾的 '\0' 字符),它在运行时计算字符串的长度,需要遍历整个字符串,对于数组,strlen 只能用于字符数组(字符串),不能用于其他类型的数组,对于指针,strlen 可以计算指针所指向的字符串的长度。
    示例:
char str[] = "hello";
printf("Length of str: %zu\n", strlen(str)); // 输出: 5

🌠 C语言中动态内存管理方式

  1. malloc:
    语法void* malloc (size_t size);
    功能:动态分配指定大小的内存块,并返回指向该内存块的指针, 分配的内存块内容是未初始化的。
    使用方法
int* ptr = (int*)malloc(sizeof(int) * 4);
if (ptr == NULL) 
{
    // 内存分配失败,处理错误
    return;
}
// 使用分配的内存
// ...
free(ptr); // 释放内存
  1. calloc:
    语法void* calloc (size_t num, size_t size);
    功能:动态分配指定数量和大小的内存块,并返回指向该内存块的指针,分配的内存块内容会被初始化为0
    使用方法
// 分配 4 个 int 型元素的内存,并初始化为 0
int *ptr = (int *)calloc(4, sizeof(int));
if (ptr == NULL) {
    // 内存分配失败,处理错误
    return;
}
// 使用分配的内存,所有元素都被初始化为 0
// ...
free(ptr); // 释放内存
  1. realloc:
    语法void* realloc (void* ptr, size_t size);
    功能:调整已分配内存块的大小,并返回指向新内存块的指针。
    • 如果新大小小于原大小,则保留原有数据;如果新大小大于原大小,则原有数据会被保留,新增部分为未初始化。
    • 如果ptrNULL,则等同于malloc(size)
      使用方法
// 先分配 4 个 int 型元素的内存
int *ptr = (int *)malloc(4 * sizeof(int));
if (ptr == NULL) 
{
    // 内存分配失败,处理错误
    return;
}
// 使用分配的内存
// ...
// 重新分配为 8 个 int 型元素的内存
int *new_ptr = (int *)realloc(ptr, 8 * sizeof(int));
if (new_ptr == NULL) 
{
    // 内存重新分配失败,处理错误
    free(ptr); // 释放原有内存
    return;
}
ptr = new_ptr; // 更新指针
// 使用新分配的内存
// ...
free(ptr); // 释放内存
  1. free:
    语法void free (void* ptr);
    功能:释放动态分配的内存块,将其返回给操作系统。注意:必须确保释放的内存块是之前使用malloc/calloc/realloc动态分配的
    • 如果ptrNULL,则该函数不执行任何操作。
      使用方法
int *ptr = (int *)malloc(4 * sizeof(int));
if (ptr == NULL) 
{
    // 内存分配失败,处理错误
    return;
}
// 使用分配的内存
// ...
free(ptr); // 释放内存
// 不能再访问已释放的内存

常见注意要点:

  1. 动态分配的内存必须在使用完毕后及时释放,否则会导致内存泄漏。
  2. 不能访问已经释放的内存块,否则会出现未定义行为。
  3. 如果分配失败,这些函数会返回NULL指针,需要进行错误处理。

🌉C++内存管理方式

C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因此C++又提出了自己的内存管理方式:通过newdelete操作符进行动态内存管理。

#include<stdlib.h>
int main()
{
	int* ptr = (int*)malloc(4 * sizeof(int));
	free(ptr);
	int* ptr2 = (int*)calloc(4, sizeof(int));
	//判断是否成功开辟空间,每个还需要检查
	int* ptr3 = (int*)realloc(ptr, 8 * sizeof(int));
	free(ptr3);
	return 0;
}

🌠new/delete操作内置类型

在 C++ 中,newdelete 操作符用于动态内存分配和释放。当使用这些操作符时,需要注意以下几点:

内置类型:

  • 对于内置类型(如 intdoublechar 等),使用 newdelete 操作符与使用 mallocfree 函数的效果是相同的。
  • 例如:
 int* ptr = new int;  // 分配一个 int 类型的内存空间
 delete ptr;         // 释放 ptr 指向的内存空间

分配内存,但没有初始化
在这里插入图片描述

  • 动态申请一个int类型的空间并初始化为10
// 动态申请一个int类型的空间并初始化为10
int* ptr2 = new int(10);
delete ptr2;

在这里插入图片描述

动态申请10个int类型的空间,并释放

 int* arr = new int[10];  //  动态申请10个int类型的空间
 delete[] arr;           // 释放 arr 指向的数组内存空间

当然,我们也可以开辟空间的时候,又进行初始化

#include<iostream>
using namespace std;

int main()
{
	// 动态申请一个int类型的空间并初始化为10
	int* ptr3 = new int[10]{ 2,3,4,5,5 };
	delete[] ptr3;

	return 0;
}

这样一部分初始化想要的值,后面默认初始化为0
在这里插入图片描述

  • 使用 newdelete操作符时,编译器会自动调用构造函数和析构函数,但对于内置类型来说,这些函数是空操作。
    在这里插入图片描述
    注意:申请和释放单个元素的空间,使用newdelete操作符,申请和释放连续的空间,使用new[]delete[],注意:匹配起来使用。
    在这里插入图片描述

🌉C与C++链表构建对比

C语言构造链表节点的方式:

struct ListNode
{
	ListNode* _next;
	int _data;
};

struct ListNode* LTCreateNode(int x)
{
	struct ListNode* newnode = (struct ListNode*)malloc(sizeof(struct ListNode));
	if (newnode == NULL) 
	{
		perror("malloc fail!");
		exit(1);
	}
	newnode->_data = x;
	newnode->_next = NULL;

	return newnode;
}

这是C++的实现:

struct ListNode
{
	ListNode* _next;
	int _data;

	ListNode(int data)
		:_next(nullptr)
		, _data(data)
	{}
};

在这里插入图片描述
前面我们知道new不仅会开空间,还会调用构造函数,析构函数的目的是初始化,delete会调用析构函数,因此即使是自定义类型,也可以使用new开空间并初始化。
因此,只要我们写好构造函数,new的使用是真香啊


🚩总结

请添加图片描述

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

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

相关文章

基于springboot+vue+Mysql的网上商城购物系统

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

蓝桥杯练习系统(算法训练)ALGO-951 预备爷的悲剧

资源限制 内存限制&#xff1a;512.0MB C/C时间限制&#xff1a;1.0s Java时间限制&#xff1a;3.0s Python时间限制&#xff1a;5.0s 问题描述 英语预备爷gzp是个逗(tu)比(hao)&#xff0c;为了在即将到来的英语的quiz中不挂科&#xff0c;gzp废寝忘食复习英语附录单词…

V23 中的新增功能:LEADTOOLS React Medical Web 查看器

LEADTOOLS (Lead Technology)由Moe Daher and Rich Little创建于1990年&#xff0c;其总部设在北卡罗来纳州夏洛特。LEAD的建立是为了使Daher先生在数码图象与压缩技术领域的发明面向市场。在过去超过30年的发展历程中&#xff0c;LEAD以其在全世界主要国家中占有的市场领导地位…

JUC常见类

背景 JUC是java.util.concurrent的简称&#xff0c;这个包里面存放的都是和多线程相关的类&#xff0c;在面试中非常的重要 目录 1.Callable接口 2.ReentrantLock 3.信号量Semaphore 4.CountDownLatch 5.集合的线程安全问题 1.Callable接口 1.1.认识Callable接口 &#x…

phpstudy 搭建 upload-labs 文件上传靶场

phpstudy 搭建靶场&#xff1a;下载安装好phpstudy后&#xff0c;下载靶场源码&#xff1a; upload-labs下载地址&#xff1a; https://github.com/c0ny1/upload-labs 下载完压缩文件&#xff0c;解压文件&#xff0c;解压后的文件夹命名为upload--labs 将解压后到文件夹放…

爱普生晶振在物联网LoRa通讯中的应用

LoRa 是LPWAN通信技术中的一种&#xff0c;是美国Semtech公司采用和推广的一种基于扩频技术的超远距离无线传输方案。这一方案改变了以往关于传输距离与功耗的折衷考虑方式&#xff0c;为用户提供一种简单的能实现远距离、长电池寿命、大容量的系统&#xff0c;进而扩展传感网络…

频分复用系统设计及其MATLAB实现

引言 随着通信技术的飞速发展&#xff0c;通信系统的容量需求不断增长。频分复用&#xff08;Frequency Division Multiplexing, FDM&#xff09;作为一种重要的多路复用技术&#xff0c;被广泛应用于现代通信系统中。本文将介绍频分复用系统的设计原理&#xff0c;并展示如何…

springboot 自动配置源码解读

什么是自动装配 当我们程序依赖第三方功能组件时&#xff0c;不需要手动将这些组件类加载到IOC容器中。例如 当程序需要用到redis时&#xff0c;在pom.xml文件中引入依赖&#xff0c;然后使用依赖注入的方式直接从IOC容器中拿到相应RedisTemplate实例。 SpringBootApplication …

cuda非root用户安装及mamba库安装报错问题

1、先检查是不是cuda版本的问题&#xff08;cuda≥11.6&#xff09; 非root用户安装指南&#xff1a; &#xff08;1&#xff09;先去NVIDIA官网CUDA Toolkit Archive | NVIDIA Developer 选一个版本 查看architecture的命令 uname -m 查看version的命令 lsb_release -a 下…

探索AIGC技术:创新、挑战与责任

&#x1f3a5; 个人主页&#xff1a;Dikz12&#x1f4d5;格言&#xff1a;那些在暗处执拗生长的花&#xff0c;终有一日会馥郁传香欢迎大家&#x1f44d;点赞✍评论⭐收藏 #如何看待AIGC技术&#xff1f; 目录 AIGC简单介绍 创新 责任 未来展望和挑战 AIGC简单介绍 A…

34.基础乐理-简谱需要移调吗?

首先需要具备 首调 与 固定调的知识&#xff0c;才能理解&#xff0c;以两只老虎为例子&#xff0c;如下图&#xff1a; 首调&#xff1a;可以看到C大调、D大调、E大调三种方式的乐谱&#xff0c;记录的数字&#xff0c;记录的唱名&#xff0c;都是1231&#xff0c;唯一不同的…

深度学习中的归一化:BN,LN,IN,GN的优缺点

目录 深度学习中归一化的作用常见归一化的优缺点 深度学习中归一化的作用 加速训练过程 归一化可以加速深度学习模型的训练过程。通过调整输入数据的尺度&#xff0c;归一化有助于改善优化算法的收敛速度。这是因为归一化后的数据具有相似的尺度&#xff0c;使得梯度下降等优化…

开源框架 NanUI 项目宣布将暂停开发,作者转行卖钢材

NanUI 界面组件是一个开源的 .NET 窗体应用程序界面框架&#xff0c;适用于希望使用 HTML5 / CSS3 等前端技术来构建 Windows 窗体应用程序用户界面的 .NET 开发人员。 该项目的作者林选臣日前在 GitHub 上发布了停更公告&#xff0c;称因去年被裁员失业&#xff0c;他目前已经…

VBA技术资料MF147:从Excel运行PowerPoint演示文稿

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。“VBA语言専攻”提供的教程一共九套&#xff0c;分为初级、中级、高级三大部分&#xff0c;教程是对VBA的系统讲解&#…

毫米波雷达多人呼吸心跳检测MATLAB仿真

本文基于TI的IWR1642毫米波雷达 2T4R MIMO阵列&#xff0c;通过实际采集数据算法仿真&#xff0c;实现多人呼吸心跳检测。 文章末尾给出了本文的仿真代码。 主要内容包含&#xff1a; &#xff08;1&#xff09;雷达参数设定 &#xff08;2&#xff09;ADC数据导入 &#xff08…

uniapp0基础编写安卓原生插件和调用第三方jar包和编写语音播报插件之使用jar包插件

前言 如果你不会编写安卓插件,你可以先看看我之前零基础的文章(uniapp0基础编写安卓原生插件和调用第三方jar包和编写语音播报插件之零基础编写安卓插件), 我们使用第三方包,jar包编写安卓插件 开始 把依赖包,放到某个模块的/libs目录(myTestPlug/libs) 还要到build…

采访OpenAI奥特曼:预计会有几个不同版本的通用人工智能,并且各有擅长 | 最新快讯

在我与山姆奥特曼&#xff08;Sam Altman&#xff09;的短暂会面中&#xff0c;有许多瞬间让我更加了解了这位 OpenAI CEO 的世界观。 第一个瞬间是他指着我的 iPhone SE 说&#xff1a;“这是最好的 iPhone。”不过&#xff0c;展示其更清晰愿景的瞬间是&#xff0c;他描绘了人…

运维的利器--监控--zabbix--第二步:建设--选择中文--导致乱码问题

文章目录 问题原因解决方法1解决方法2&#xff1a;修改图形的数据展示&#xff08;默认字体有问题&#xff09; 问题 点击对应主机的【图形】即可看到以下乱码情况 原因 上述的图标数据&#xff0c;下面的小白框表示乱码含义&#xff0c;是因为我们改了zabbix的 语言为中文…

c语言——二叉树

目录 目录 二叉树关键概念理解 一颗拥有1000个结点的树度为4&#xff0c;则它的最小深度是&#xff1f; 那么对于二叉树&#xff0c;只掌握这些是远远不够的&#xff0c;我们还需要掌握几个最基本的经典问题&#xff0c; 求二叉树大小 求叶子结点个数 求深度 求第k层的…

如何快速的追加文章的内容(在不知道内容的情况下)

首先&#xff0c;需要用到的这个工具&#xff1a; 度娘网盘 提取码&#xff1a;qwu2 蓝奏云 提取码&#xff1a;2r1z 1、打开工具&#xff0c;切换到文章模块下&#xff0c;快捷键&#xff1a;Ctrl1 2、新建一个文章做演示&#xff0c;001 3、添加一个内容&#xff0c;就随…