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

embedded/2024/9/20 3:59:12/ 标签: c++, java, 开发语言, 云计算, 服务器

请添加图片描述

文章目录

  • 📝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类型的空间并初始化为10int* 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.ppmy.cn/embedded/29135.html

相关文章

复杂prompt组成

chatGpt像一个具有海量知识的婴儿&#xff0c;了解的知识足够多&#xff0c;但是还不够足够智能。为了能够让gpt能精准回复&#xff0c;一是将问题尽可能描述清楚&#xff0c;帮助gpt理解问题&#xff0c;二是给出一些有效的筛选条件&#xff0c;帮助gpt去从海量知识中筛选可能…

Python版本管理工具-pyenv

Pyenv是一个Python版本管理工具。 Pyenv允许用户在同一台机器上安装多个版本的Python&#xff0c;并能够轻松切换使用这些版本。 一、安装 Mac下直接使用Homebrew安装 # 更新 Homebrew 的软件列表 brew update # 安装pyenv brew install pyenv# 验证是否安装成功 pyenv -v# …

spring bean的生命周期你了解么

Spring Bean的生命周期是指在Spring容器中创建、初始化、使用和销毁Bean实例的整个过程。理解Spring Bean的生命周期对于开发者来说非常重要&#xff0c;因为它涉及到在不同的生命周期阶段执行自定义逻辑的机会。下面是关于Spring Bean生命周期的详细解释&#xff0c;包括各个阶…

【设计模式】之模板方法模式

系列文章目录 【设计模式】之策略模式 【设计模式】之责任链模式 文章目录 系列文章目录 前言 一、什么是模板方法模式 定义 角色 二、为什么要使用模板方法模式 优点 缺点 三、案例 普通案例 模拟Servlet过程案例 总结 前言 今天给大家介绍23种设计模式中的模板方法模式&a…

LeetCode-网络延迟时间(Dijkstra算法)

每日一题 今天刷到一道有关的图的题&#xff0c;需要求单源最短路径&#xff0c;因此使用Dijkstra算法。 题目要求 有 n 个网络节点&#xff0c;标记为 1 到 n。 给你一个列表 times&#xff0c;表示信号经过 有向 边的传递时间。 times[i] (ui, vi, wi)&#xff0c;其中 …

抖音小店运营实战班,全新升级 从零到进阶精通 分享月销百万小店核心秘密

课程内容&#xff1a; 1 2024抖音电商发展趋势及抖店运营策略(直播2024 0412).mp4 2 1-1抖音小店入驻流程(直播2024 04 12),mp4 3 1-2个体店铺VS企业店铺有什么区别(直播20240412).mp4 4 1-3抖音小店店铺搭建(直播2024 04 12).mp4 5 2-1-如何避免违禁词(附违禁词大全)(直播…

纯血鸿蒙APP实战开发——评论组件案例实现

介绍 评论组件在目前市面上的短视频app中是一种很常见的场景&#xff0c;本案例使用全局状态保留能力弹窗来实现评论组件。点击评论按钮弹出评论组件&#xff0c;点击空白处隐藏该组件&#xff0c;再次点击评论按钮则会恢复上一次浏览的组件状态。 效果图预览 使用说明 点击…

vue2实现面包屑功能

目录 1. store/index.js 2. router/index.js 3. Header.vue 在Vue 2中实现面包屑导航是一种常见的前端实践,它可以帮助用户了解当前页面在网站结构中的位置,并快速导航到上一级或根目录。以下是使用Vue 2实现面包屑导航的基本步骤: 1. store/index.js state中定义一个面…

YOLOv8 是一个相对较新的目标检测算法

YOLOv8 是一个相对较新的目标检测算法&#xff0c;它尚未有官方的代码发布时&#xff08;截至当前知识截止日期&#xff09;&#xff0c;但基于 YOLO 系列的演进和常见的注意力机制&#xff08;如 SE&#xff08;Squeeze-and-Excitation&#xff09;注意力&#xff09;&#xf…

sklearn和torch计算的r2 score不一样

检查一下函数参数的位置&#xff0c;预测值和真实值位置不一样&#xff0c;可以参考函数定义 torch_r2score torch_r2(pred, y) sklearn_r2score r2_score(y, pred) https://pytorch.org/torcheval/main/generated/torcheval.metrics.functional.r2_score.html Parameters: …

Linux开发板 FTP 服务器移植与搭建

VSFTPD&#xff08;Very Secure FTP Daemon&#xff09;是一个安全、稳定且快速的FTP服务器软件&#xff0c;广泛用于Unix和Linux操作系统。它以其轻量级、高效和易于配置而受到赞誉。VSFTPD不仅支持标准的FTP命令和操作&#xff0c;还提供了额外的安全特性&#xff0c;如匿名F…

springcloud自定义全局异常

自行创建一个实体类 /*** 全局异常处理类**/ ControllerAdvice public class GlobalExceptionHandler {ExceptionHandler(Exception.class) ResponseBody public Result error(Exception e){e.printStackTrace(); return Result.fail();}/*** 自定义异常处理方法* param e * re…

RDD编程初级实践

参考链接 spark入门实战系列--8MLlib spark 实战_mob6454cc68310b的技术博客_51CTO博客https://blog.51cto.com/u_16099212/7454034 Spark和Hadoop的安装-CSDN博客https://blog.csdn.net/weixin_64066303/article/details/138021948?spm1001.2014.3001.5501 1. spark-shell…

vue查询数据里面指定的数据

vue查询数据里面指定的数据 可以通过find函数进行查找&#xff0c; 以下是find函数的用法&#xff1a; this.meal_tables.find(item > item.price 10)查询的结果是复合条件的数据 注意&#xff1a; this.meal_tables为一个对象数组 meal_tables: []

Macs Fan Control Pro for mac激活版:macOS 平台的风扇控制软件

Macs Fan Control Pro是一款用于 macOS 平台的风扇控制软件&#xff0c;它允许用户监控和调整 Mac 电脑的风扇转速。以下是该软件的一些特点和功能&#xff1a; Macs Fan Control Pro for mac激活版下载 风扇监控&#xff1a;Macs Fan Control Pro 提供实时的风扇转速监控&…

大数据机器学习:常见模型评估指标

大数据机器学习&#xff1a;常见模型评估指标 一.模型评估综述 1.1 什么是模型评估 模型评估是指在机器学习中&#xff0c;对于一个具体方法输出的最终模型&#xff0c;使用一些指标和方法来评估它的泛化能力。这一步通常在模型训练和模型选择之后&#xff0c;正式部署模型之…

【软件工程】【第二章可行性分析与需求分析】

关键字&#xff1a; 可行性分析、甘特图、数据流图、数据字典、需求分析、E-R图、状态转换图

Agent AI智能体:如何借助机器学习引领科技新潮流

文章目录 &#x1f4d1;前言一、Agent AI智能体的基本概念二、Agent AI智能体的技术进步2.1 机器学习技术2.2 自适应技术2.3 分布式计算与云计算 三、Agent AI智能体的知识积累3.1 知识图谱3.2 迁移学习 四、Agent AI智能体的挑战与机遇4.1 挑战4.2 机遇 小结 &#x1f4d1;前言…

2.Neo4j的搭建启动

Graph Database 图数据库 版本对应关系 官网都是高版本&#xff0c;推荐使用下载地址可以找到社区老版本&#xff1a; https://we-yun.com/doc/neo4j/ neo4j.bat 启动脚本 cypher-shell.bat 执行CQL语句的。 import文件夹可以放入excel,csv等数据文件&#xff0c;导入到…

【题解 | 思维】三元组中心问题

三元组中心问题 注意点&#xff1a; 同一个位置的元素&#xff0c;不管以它为中心能组成多少个三元组&#xff0c;只记一个不同索引位置的相同元素&#xff0c;算多个中心元素。 常规暴力 import java.util.Scanner;public class Main {public static void main(String[] args…