当前位置: 首页 > news >正文

Windows 重启 explorer 的正确做法

目录

一、关于 Restart Manager

二、重启管理器实例

三、完整实现代码和测试


本文属于原创文章,转载请注明出处:

https://blog.csdn.net/qq_59075481/article/details/136179191。

我们往往使用 TerminateProcess 并传入 PID 和特殊结束代码 1 或者 taskkill /f /im 等方法重启资源管理器( explorer.exe ),其实这是不正确的。我们应该使用重启管理器接口来重启 explorer 进程。

一、关于 Restart Manager

重启管理器 API 可以消除或减少完成安装或更新所需的系统重启次数。软件更新在安装或更新期间需要重启系统的主要原因是,正在运行的应用程序或服务当前正在使用某些正在更新的文件。 重启管理器允许关闭并重启除关键系统服务外的所有服务 。这会释放正在使用的文件,并允许完成安装操作。

安装程序可以使用重启管理器来注册应在安装应用程序或更新期间替换的文件。然后,在后续更新或安装期间,安装程序可以使用重启管理器来确定哪些文件因当前正在使用而无法更新。重启管理器可以关闭并重启当前使用这些文件的非关键服务或应用程序。安装程序可以指示重启管理器根据正在使用的文件、进程 ID (PID) 或 Windows 服务的短名称来关闭和重启应用程序或服务。

二、重启管理器实例

实现一个简易的重启管理器实例主要需要 RmStartSession 、RmRegisterResources、RmShutdown、RmEndSession 等函数。

首先我们需要在代码中引入头文件:

#include <restartmanager.h>
#pragma comment(lib, "Rstrtmgr.lib")

然后我们用 RmStartSession 创建会话。其中会话的密钥是 GUID ,为其分配缓冲区时候需要计算好缓冲区大小。

#define RM_SESSIONKEY_LEN sizeof(GUID) * 2 + 1
DWORD dwRmStatus = 0;
DWORD dwSessionHandle = 0;
WCHAR strSessionKey[RM_SESSIONKEY_LEN] = { 0 };
dwRmStatus = RmStartSession(&dwSessionHandle, NULL, strSessionKey);
if (ERROR_SUCCESS != dwRmStatus)
{std::cerr << "RmStartSession failed: " << std::dec << dwRmStatus << std::endl;return -1;
}

随后需要将要重启的进程信息放到 RM_UNIQUE_PROCESS 结构体数组中,该结构体如下:

typedef struct _RM_UNIQUE_PROCESS {DWORD    dwProcessId;FILETIME ProcessStartTime;
} RM_UNIQUE_PROCESS, *PRM_UNIQUE_PROCESS;

第一个是进程的 PID,第二个是进程的创建时间,使用 GetProcessTimes 函数的 lpCreationTime 参数获取。

BOOL GetProcessTimes([in]  HANDLE     hProcess,[out] LPFILETIME lpCreationTime,[out] LPFILETIME lpExitTime,[out] LPFILETIME lpKernelTime,[out] LPFILETIME lpUserTime
);

我使用 Toolhelp32 枚举 explorer 进程,并生成 RM_UNIQUE_PROCESS 结构体数组。

BOOL GetShellProcessRmInfoEx(PRM_UNIQUE_PROCESS* lpRmProcList, DWORD_PTR* lpdwCountNum
)
{PROCESSENTRY32W pe32 = { 0 };FILETIME lpCreationTime = { 0 };FILETIME lpExitTime = { 0 };FILETIME lpKernelTime = { 0 };FILETIME lpUserTime = { 0 };HANDLE hProcess = nullptr;RM_UNIQUE_PROCESS tpProc = { 0 };std::vector<RM_UNIQUE_PROCESS> RmProcVec;SIZE_T VecLength = 0;// 在使用这个结构前,先设置它的大小pe32.dwSize = sizeof(pe32);// 给系统内所有的进程拍个快照HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);if (hProcessSnap == INVALID_HANDLE_VALUE){std::cerr << "CreateToolhelp32Snapshot 调用失败." << std::endl;return FALSE;}// 遍历进程快照,轮流显示每个进程的信息BOOL bMore = Process32FirstW(hProcessSnap, &pe32);while (bMore){if (!_wcsicmp(pe32.szExeFile, L"explorer.exe")){hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pe32.th32ProcessID);if (hProcess != nullptr){memset(&lpCreationTime, 0, sizeof(FILETIME));if (GetProcessTimes(hProcess,&lpCreationTime, &lpExitTime,&lpKernelTime, &lpUserTime) == TRUE){tpProc.dwProcessId = pe32.th32ProcessID;tpProc.ProcessStartTime = lpCreationTime;RmProcVec.push_back(tpProc);}CloseHandle(hProcess);hProcess = nullptr;}}bMore = Process32NextW(hProcessSnap, &pe32);}// 清除 snapshot 对象CloseHandle(hProcessSnap);VecLength = RmProcVec.size();if (VecLength != 0 && VecLength < (SIZE_T)0xf4236u){RM_UNIQUE_PROCESS* lprmUniqueProc = new(std::nothrow) RM_UNIQUE_PROCESS[VecLength];if (lprmUniqueProc != nullptr){SIZE_T rSize = VecLength * sizeof(RM_UNIQUE_PROCESS);if (rSize < (SIZE_T)0xC80000u && rSize > 0){if (!memcpy_s(lprmUniqueProc, rSize, &RmProcVec[0], rSize)){*lpdwCountNum = VecLength;*lpRmProcList = lprmUniqueProc;return TRUE;}}else {std::cerr << "Vector Size to large!" << std::endl;}}else {std::cerr << "Alloc memory failed!" << std::endl;}}else {std::cerr << "Vector Size to large!" << std::endl;}return FALSE;
}

使用 RmRegisterResources 函数将资源注册到重启管理器会话。重启管理器使用向会话注册的资源列表来确定必须关闭和重启哪些应用程序和服务。 可以通过文件名、服务短名称或描述正在运行的应用程序 RM_UNIQUE_PROCESS 结构来标识资源。参数解释如下:

参数解释

代码如下: 

dwRmStatus = RmRegisterResources(dwSessionHandle,0, NULL, dwNum, lpRmProcList, 0, NULL);
if (ERROR_SUCCESS != dwRmStatus)
{std::cerr << "RmRegisterResources failed: " << std::dec << dwRmStatus << std::endl;RmProcMemFree(lpRmProcList, lpdwCountNum);return -1;
}

然后使用 RmShutdown 就可以请求结束进程,可以指定第二个参数来选择强制或者非强制两种状态。

完成任务后使用 RmRestart 重新启动被关闭的进程即可,程序会尝试恢复并刷新状态。

所有操作完成后,使用 RmEndSession 关闭会话句柄。

三、完整实现代码和测试

完整代码如下:

#include <iostream>
#include <Windows.h>
#include <restartmanager.h>
#include <TlHelp32.h>
#include <vector>#pragma comment(lib, "Rstrtmgr.lib")#define RM_SESSIONKEY_LEN sizeof(GUID) * 2 + 1BOOL GetShellProcessRmInfoEx(PRM_UNIQUE_PROCESS* lpRmProcList,DWORD_PTR* lpdwCountNum
);
void RmProcMemFree(PRM_UNIQUE_PROCESS lpRmProcList,DWORD_PTR lpdwCountNum
);void RmWriteStatusCallback(UINT nPercentComplete
);int main()
{DWORD dwRmStatus = 0;DWORD dwSessionHandle = 0;WCHAR strSessionKey[RM_SESSIONKEY_LEN] = { 0 };dwRmStatus = RmStartSession(&dwSessionHandle, NULL, strSessionKey);if (ERROR_SUCCESS != dwRmStatus){std::cerr << "RmStartSession failed: " << std::dec << dwRmStatus << std::endl;return -1;}PRM_UNIQUE_PROCESS lpRmProcList = nullptr;DWORD_PTR lpdwCountNum = 0;if (!GetShellProcessRmInfoEx(&lpRmProcList, &lpdwCountNum)){std::cerr << "GetShellProcessRmInfoEx failed."<< std::endl;return -1;}UINT dwNum = static_cast<UINT>(lpdwCountNum);std::cout << "Process Count: " << dwNum << std::endl;std::cout << "Shell PID: "<< std::endl;for (UINT i = 0; i < dwNum; i++){std::cout << " > " << lpRmProcList[i].dwProcessId << std::endl;}dwRmStatus = RmRegisterResources(dwSessionHandle,0, NULL, dwNum, lpRmProcList, 0, NULL);if (ERROR_SUCCESS != dwRmStatus){std::cerr << "RmRegisterResources failed: " << std::dec << dwRmStatus << std::endl;RmProcMemFree(lpRmProcList, lpdwCountNum);return -1;}dwRmStatus = RmShutdown(dwSessionHandle, RmForceShutdown, RmWriteStatusCallback);if (ERROR_SUCCESS != dwRmStatus){std::cerr << "RmShutdown failed: " << std::dec << dwRmStatus << std::endl;// return -1;}Sleep(5000);dwRmStatus = RmRestart(dwSessionHandle, 0, RmWriteStatusCallback);if (ERROR_SUCCESS != dwRmStatus){std::cerr << "RmRestart failed: " << std::dec << dwRmStatus << std::endl;// return -1;}dwRmStatus = RmEndSession(dwSessionHandle);if (ERROR_SUCCESS != dwRmStatus){std::cerr << "RmEndSession failed: " << std::dec << dwRmStatus << std::endl;// return -1;}RmProcMemFree(lpRmProcList, lpdwCountNum);return 0;
}void RmWriteStatusCallback(UINT nPercentComplete
)
{std::cout << "Task completion level: " << std::dec << nPercentComplete << std::endl;
}BOOL GetShellProcessRmInfoEx(PRM_UNIQUE_PROCESS* lpRmProcList, DWORD_PTR* lpdwCountNum
)
{PROCESSENTRY32W pe32 = { 0 };FILETIME lpCreationTime = { 0 };FILETIME lpExitTime = { 0 };FILETIME lpKernelTime = { 0 };FILETIME lpUserTime = { 0 };HANDLE hProcess = nullptr;RM_UNIQUE_PROCESS tpProc = { 0 };std::vector<RM_UNIQUE_PROCESS> RmProcVec;SIZE_T VecLength = 0;// 在使用这个结构前,先设置它的大小pe32.dwSize = sizeof(pe32);// 给系统内所有的进程拍个快照HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);if (hProcessSnap == INVALID_HANDLE_VALUE){std::cerr << "CreateToolhelp32Snapshot 调用失败." << std::endl;return FALSE;}// 遍历进程快照,轮流显示每个进程的信息BOOL bMore = Process32FirstW(hProcessSnap, &pe32);while (bMore){if (!_wcsicmp(pe32.szExeFile, L"explorer.exe")){hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pe32.th32ProcessID);if (hProcess != nullptr){memset(&lpCreationTime, 0, sizeof(FILETIME));if (GetProcessTimes(hProcess,&lpCreationTime, &lpExitTime,&lpKernelTime, &lpUserTime) == TRUE){tpProc.dwProcessId = pe32.th32ProcessID;tpProc.ProcessStartTime = lpCreationTime;RmProcVec.push_back(tpProc);}CloseHandle(hProcess);hProcess = nullptr;}}bMore = Process32NextW(hProcessSnap, &pe32);}// 清除 snapshot 对象CloseHandle(hProcessSnap);VecLength = RmProcVec.size();if (VecLength != 0 && VecLength < (SIZE_T)0xf4236u){RM_UNIQUE_PROCESS* lprmUniqueProc = new(std::nothrow) RM_UNIQUE_PROCESS[VecLength];if (lprmUniqueProc != nullptr){SIZE_T rSize = VecLength * sizeof(RM_UNIQUE_PROCESS);if (rSize < (SIZE_T)0xC80000u && rSize > 0){if (!memcpy_s(lprmUniqueProc, rSize, &RmProcVec[0], rSize)){*lpdwCountNum = VecLength;*lpRmProcList = lprmUniqueProc;return TRUE;}}else {std::cerr << "Vector Size to large!" << std::endl;}}else {std::cerr << "Alloc memory failed!" << std::endl;}}else {std::cerr << "Vector Size to large!" << std::endl;}return FALSE;
}void RmProcMemFree(PRM_UNIQUE_PROCESS lpRmProcList, DWORD_PTR lpdwCountNum)
{__try{DWORD_PTR dwCountNum = lpdwCountNum;if (lpRmProcList != nullptr && dwCountNum > 0){while (--dwCountNum){if (IsBadWritePtr(&lpRmProcList[dwCountNum], sizeof(RM_UNIQUE_PROCESS))){throw(L"BadWritePtr event!");break;}memset(&lpRmProcList[dwCountNum], 0, sizeof(RM_UNIQUE_PROCESS));}delete[] lpRmProcList;}}__except (EXCEPTION_EXECUTE_HANDLER){// 处理 SEH 异常std::cerr << "Error access violation." << std::endl;exit(-1);}
}

测试效果如图所示:

测试结果截图

测试内容:首先结束所有 explorer 进程,随后重启进程。 


本文属于原创文章,转载请注明出处:

https://blog.csdn.net/qq_59075481/article/details/136179191。

文章发布于:2024.02.19,更新于:2024.02.19.

相关文章:

Windows 重启 explorer 的正确做法

目录 一、关于 Restart Manager 二、重启管理器实例 三、完整实现代码和测试 本文属于原创文章&#xff0c;转载请注明出处&#xff1a; https://blog.csdn.net/qq_59075481/article/details/136179191。 我们往往使用 TerminateProcess 并传入 PID 和特殊结束代码 1 或者…...

linux基础学习(10):基本权限与相关命令

1.基本权限 用ls -l查看当前目录文件时&#xff0c;可以看到文件的基本权限 其由10位组成&#xff0c;其中&#xff1a; 第1位&#xff1a;代表文件类型。 - d lbc普通文件目录文件软链接文件块设备文件&#xff0c;也就是硬盘等存储设备的文件字符设备文件&#xff0c;是鼠…...

木马植入方式及防范手段

木马植入方式&#xff1a; 1. 诱骗下载和安装&#xff1a;通过欺骗、社交工程等手段&#xff0c;诱使用户下载和安装包含木马的软件或文件。 2. 隐秘附加&#xff1a;将木马隐藏在合法软件的背后&#xff0c;并伴随软件一起安装&#xff0c;用户在不知情的情况下也会安装木马。…...

Unity3D中刚体、碰撞组件、物理组件的区别详解

前言 Unity3D提供了丰富的功能和组件&#xff0c;其中包括刚体、碰撞组件和物理组件。这些组件在游戏开发中起着非常重要的作用&#xff0c;能够让游戏世界更加真实和有趣。本文将详细介绍这三种组件的区别以及如何在Unity3D中实现它们。 对惹&#xff0c;这里有一个游戏开发…...

Java实现Redis延时队列

“如何实现Redis延时队列”这个面试题应该也是比较常见的&#xff0c;解答如下&#xff1a; 使用sortedset&#xff08;有序集合&#xff09; &#xff0c;拿时间戳作为 score &#xff0c;消息内容作为key 调用 zadd 来生产消息&#xff0c;消费者用zrangebyscore 指令获取 N …...

Selenium折线图自动化测试

目录 获取折线图echarts实例 获取折线图实例锚点的坐标 通过echarts实例的getOption()方法获取坐标数据 将折线图坐标点转换为像素坐标值 整合折线图坐标数据 根据折线图坐标计算出锚点相对于浏览器中的坐标 计算canvas画布原点的坐标 计算折线图相对于浏览器的坐标 使用…...

<网络安全>《41 网络攻防专业课<第七课 - IIS上传和Tomcat弱口令漏洞攻击与防范>》

1 中间件PUT漏洞介绍 中间件包括apache、tomcat、IIS、weblogic等&#xff0c;这些中间件可以设置支持的HTTP方法。&#xff08;HTTP方法包括GET、POST、HEAD、DELETE、PUT、OPTIONS等&#xff09; 每一个HTTP方法都有其对应的功能&#xff0c;在这些方法中&#xff0c;PUT可…...

云计算基础-虚拟化概述

虚拟化概述 虚拟化是一种资源管理技术&#xff0c;能够将计算机的各种实体资源&#xff08;如CPU、内存、磁盘空间、网络适配器等&#xff09;予以抽象、转换后呈现出来并可供分割、组合为一个或多个逻辑上的资源。这种技术通过在计算机硬件上创建一个抽象层&#xff0c;将单台…...

ElementUI +++ Echarts面试题答案汇总

官网地址&#xff1a;http://element-cn.eleme.io/#/zh-CN ElementUI是一套基于VUE2.0的桌面端组件库&#xff0c;ElementUI提供了丰富的组件帮助开发人员快速构建功能强大、风格统一的页面。 ElementUi是怎么做表单验证的&#xff1f;在循环里对每个input验证怎么做呢&#x…...

notepad++打开文本文件乱码的解决办法

目录 第一步 在编码菜单栏下选择GB2312中文。如果已经选了忽略这一步 第二步 点击编码&#xff0c;红框圈出来的一个个试。我切换到UTF-8编码就正常了。 乱码如图。下面分享我的解决办法 第一步 在编码菜单栏下选择GB2312中文。如果已经选了忽略这一步 第二步 点击编码&#…...

道可云元宇宙每日资讯|上海开放大学发布“智慧学习中心元宇宙”

道可云元宇宙每日简报&#xff08;2024年2月19日&#xff09;讯&#xff0c;今日元宇宙新鲜事有&#xff1a; 上海开放大学发布“智慧学习中心元宇宙” 上海开放大学首次发布了“智慧学习中心元宇宙”&#xff0c;初步构筑了上海开放大学5g元宇宙孪生学习环境&#xff0c;13所…...

压缩感知(Compressed Sensing,CS)的基础知识

压缩感知&#xff08;Compressed Sensing&#xff0c;CS&#xff09;是一种用于信号处理的技术&#xff0c;旨在以少于奈奎斯特采样定理所要求的样本频率来重构信号。该技术利用信号的稀疏性&#xff0c;即信号可以用较少的非零系数表示。压缩感知在图像获取中的应用使得在采集…...

如何系统地学习Python

建议系统学习Python的途径遵循理论与实践相结合的教学方法。以下是一个分阶段的学习计划&#xff1a; 阶段一&#xff1a;基础知识 理解Python的特点&#xff1a; 认识Python的历史与设计哲学。学习Python的基本语法和运行环境。 安装Python&#xff1a; 学习如何在不同操作系…...

SMT2020:半导体制造流程标准仿真测试数据介绍

文章目录 问题背景SMT2020 涉及的主要功能1. 包含多种仿真模型类型2. 包含非计划性动作3. 区分不同类型设备的加工速率4. 特殊的复杂操作SMT2020 数据概览1. AutoSched 仿真模型数据2. General Data 输入数据问题背景 在半导体的生产制造当中,由于晶元片及设备等的高价值性,…...

沁恒CH32V30X学习笔记11---使用外部时钟模式2采集脉冲计数

使用外部时钟模式2采集脉冲计数 使用外部触发模式 2 能在外部时钟引脚输入的每一个上升沿或下降沿计数。将 ECE 位置位时,将使用外部时钟源模式 2。使用外部时钟源模式 2 时,ETRF 被选定为 CK_PSC。ETR 引脚经过可选的反相器(ETP),分频器(ETPS)后成为 ETRP,再经过滤波…...

ffmpeg for android编译全过程与遇到的问题

编译前准备 编译环境&#xff1a;Ubuntu16&#xff0c;可自行下载VMWare最新版并百度永久许可证或在服务器上安装Ubuntu ffmpeg源码&#xff1a;ffmpeg4.2.2 NDK下载&#xff1a;Android NDK r21e 有条件的最好还是在Liunx平台下编译吧&#xff0c;Windows平台下编译坑更多…...

【无标题】力扣报错:member access within null pointer of type ‘struct ListNode‘

项目场景&#xff1a; 做单链表反转题目&#xff0c;报错&#xff1a;member access within null pointer of type ‘struct ListNode’ 题目链接:LINK 问题描述 我明明在初始化指针时候&#xff0c;已经处理了n2->next情况却依然报错 这个报错提示含义是&#xff1a;大概就…...

Qt之Qchar类的接口1

Qt类的构造函数 QChar类提供了许多个不同原型的构造函数&#xff0c;以方便不同场合下使用。 QChar(); //构造一个空字符&#xff0c;即‘\0’ QChar(char ch); //由字符数据ch构造 QChar(uchar ch)…...

vue的十大面试题详情

1 v-show与v-if区别 v-if与v-show可以根据条件的结果,来决定是否显示指定内容&#xff1a; v-if: 条件不满足时, 元素不会存在. v-show: 条件不满足时, 元素不会显示(但仍然存在). <div id"app"><button click"show !show">点我</but…...

(十四)devops持续集成开发——jenkins流水线使用pipeline方式发布项目

前言 本节内容我们使用另外一种方式pipeline实现项目的流水线部署发布&#xff0c;Jenkins Pipeline是一种允许以代码方式定义持续集成和持续交付流水线的工具。通过Jenkins Pipeline&#xff0c;可以将整个项目的构建、测试和部署过程以脚本的形式写入Jenkinsfile中&#xff…...

Vue记事本应用实现教程

文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展&#xff1a;显示创建时间8. 功能扩展&#xff1a;记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?

在建筑行业&#xff0c;项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升&#xff0c;传统的管理模式已经难以满足现代工程的需求。过去&#xff0c;许多企业依赖手工记录、口头沟通和分散的信息管理&#xff0c;导致效率低下、成本失控、风险频发。例如&#…...

Frozen-Flask :将 Flask 应用“冻结”为静态文件

Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是&#xff1a;将一个 Flask Web 应用生成成纯静态 HTML 文件&#xff0c;从而可以部署到静态网站托管服务上&#xff0c;如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

三体问题详解

从物理学角度&#xff0c;三体问题之所以不稳定&#xff0c;是因为三个天体在万有引力作用下相互作用&#xff0c;形成一个非线性耦合系统。我们可以从牛顿经典力学出发&#xff0c;列出具体的运动方程&#xff0c;并说明为何这个系统本质上是混沌的&#xff0c;无法得到一般解…...

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...

今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存

文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...

基于matlab策略迭代和值迭代法的动态规划

经典的基于策略迭代和值迭代法的动态规划matlab代码&#xff0c;实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”

2025年#高考 将在近日拉开帷幕&#xff0c;#AI 监考一度冲上热搜。当AI深度融入高考&#xff0c;#时间同步 不再是辅助功能&#xff0c;而是决定AI监考系统成败的“生命线”。 AI亮相2025高考&#xff0c;40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕&#xff0c;江西、…...

基于 TAPD 进行项目管理

起因 自己写了个小工具&#xff0c;仓库用的Github。之前在用markdown进行需求管理&#xff0c;现在随着功能的增加&#xff0c;感觉有点难以管理了&#xff0c;所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD&#xff0c;需要提供一个企业名新建一个项目&#…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配

目录 一、C 内存的基本概念​ 1.1 内存的物理与逻辑结构​ 1.2 C 程序的内存区域划分​ 二、栈内存分配​ 2.1 栈内存的特点​ 2.2 栈内存分配示例​ 三、堆内存分配​ 3.1 new和delete操作符​ 4.2 内存泄漏与悬空指针问题​ 4.3 new和delete的重载​ 四、智能指针…...