【重学C++】01| C++ 如何进行内存资源管理?
大家好,我是只讲技术干货的会玩code,今天是【重学C++】的第一讲,我们来学习下C++的内存管理。
与java、golang等自带垃圾回收机制的语言不同,C++并不会自动回收内存。我们必须手动管理堆上内存分配和释放,这往往会导致内存泄漏和内存溢出等问题。而且,这些问题可能不会立即出现,而是运行一段时间后,才会暴露出现,排查也很困难。因此,了解和掌握C++中的内存管理技巧和工具是非常重要的,可以提高程序性能、减少错误和增加安全性。
在C++中,将操作系统分配给程序的内存空间按照用途划分了代码段、数据段、栈、堆几个不同的区域,每个区域都有其独特的内存管理机制。
代码区是用于存储程序代码的区域,代码段在程序真正执行前就被加载到内存中,在程序执行期间,代码区内存不会被修改和释放。
由于代码区是只读的,所以会被多个进程共享。在多个进程同时执行同一个程序时,操作系统只需要将代码段加载到内存中一次,然后让多个进程共享这个内存区域即可。
数据段用于存储静态全局变量、静态局部变量和静态常量等静态数据。在程序运行期间,数据段的大小固定不变,但其内容可以被修改。按照变量是否被初始化。数据段可分为已初始化数据段和未初始化数据段。
C++中函数调用以及函数内的局部变量的使用,都是通过栈这个内存分区实现的。栈分区由操作系统自动分配和释放,是一种"后进先出"的一种内存分区。每个栈的大小是固定的,一般只有几MB,所以如果栈变量太大,或者函数调用嵌套太深,容易发生栈溢出(stack overflow)。
先来一段示例代码,看看C++是如何使用栈进行使用栈来进行函数调用的。
#include <iostream>
void inner(int a) {
std::cout << a << std::endl;
}
void outer(int n) {
int a = n + 1;
inner(a);
}
int main() {
outer(4);
}
上面这段代码运行过程中的栈变化如下图
每当程序调用一个函数时,该函数的参数、局部变量和返回地址等信息会被压入栈中。当函数执行完毕,再将这些信息从栈中弹出。根据之前压入的外层调用者压入栈的返回地址,返回到外层调用者未执行的代码继续执行。
本地变量是直接存储在栈上的,当函数执行完成后,这些变量占用的内存就会被释放掉了。前面例子中的本地变量是简单类型,在C++中称为POD类型。对于带有构造和析构函数的非POD类型变量,栈上的内存分配同样有效。编译器会在合适的时机,插入对构造函数和析构函数的调用。
这里有个问题,当函数执行发生异常时,析构函数还会被调用吗?
答案是会的,C++对于发生异常时对析构函数的调用称为"栈展开"。通过下面这段代码演示栈展开。
#include <iostream>
#include <string>
class Obj {
public:
std::string name_;
Obj(const std::string& name):name_(name){std::cout << "Obj() " << name_ << std::endl;};
~Obj() {std::cout << "~Obj() " << name_ << std::endl;};
};
void bar() {
auto o = Obj{"bar"};
throw "bar exception";
}
int main() {
try {
bar();
} catch (const char* e) {
std::cout << "catch Exception: " << e << std::endl;
}
}
执行代码的结果是:
Obj() bar
~Obj() bar
catch Exception: bar exception
可以发现,发生异常时,bar
函数中的本地变量o
还是能被正常析构。
栈展开的过程实际上是异常发生时,匹配catch子句的过程。
在这期间,栈上所有的对象都会被自动析构。
堆是C++中用来存储动态分配内存的内存分区,堆内存的分配和释放需要手动管理,可以通过new/delete或malloc/free等函数进行分配和释放。堆内存的大小通常是不固定的,当我们需要动态分配内存时,就可以使用堆内存。
堆内存由程序员手动分配和释放,因此使用堆内存需要注意内存泄漏和内存溢出等问题。当程序员忘记释放已分配的内存时,会导致内存泄漏问题。而当申请的堆内存超过了操作系统所分配给进程的内存限制时,会导致内存溢出问题。
C++程序绝大多数的内存泄露,都是由于忘记调用delete/free来释放堆上的资源。
还是上代码
#include <iostream>
#include <string>
class Obj {
public:
std::string name_;
Obj(const std::string& name):name_(name){std::cout << "Obj() " << name_ << std::endl;};
~Obj() {std::cout << "~Obj() " << name_ << std::endl;};
};
Obj* makeObj() {
Obj* obj = nullptr;
try {
obj = new Obj{"makeObj"};
...
} catch(...) {
delete obj;
throw;
}
return obj;
}
Obj* foo() {
Obj* obj = nullptr;
try {
obj = makeObj();
...
} catch(...) {
delete obj;
}
return obj;
}
int main() {
Obj* obj = foo();
...
delete obj;
}
可以看到,由makeObj
函数创建的堆变量obj
, 在每个获取该变量的上层调用中,都需要关心对该变量的处理。这无疑极大得增加了开发者的心智负担。
想在堆上创建对象,又不想处理这么复杂的内存释放操作。C++没有像java、golang其他语言创建一套垃圾回收机制,而是采用了一种特有的资源管理方式 --- RAII(Resource Acquisition Is Initialization,资源获取即初始化)。
RAII利用栈对象在作用域结束后会自动调用析构函数的特点,通过创建栈对象来管理资源。在栈对象构造函数中获取资源,在栈对象析构函数中负责释放资源,以此保证资源的获取和释放。
下面给出一个通过RAII来自动释放堆内存的例子
#include <iostream>
class AutoIntPtr {
public:
AutoIntPtr(int* p = nullptr) : ptr(p) {}
~AutoIntPtr() { delete ptr; }
int& operator*() const { return *ptr; }
int* operator->() const { return ptr; }
private:
int* ptr;
};
void foo() {
AutoIntPtr p(new int(5));
std::cout << *p << std::endl; // 5
}
int main() {
foo();
}
上面例子中,AutoIntPtr
类封装了一个动态分配的int
类型的指针,它的构造函数用于获取资源(ptr = p),析构函数用于释放资源(delete ptr)。当AutoIntPtr
超出作用域时,自动调用析构函数来释放所包含的资源。
基于RAII,C++11引入了std::unique_ptr
和std::shared_ptr
等智能指针用于内存管理类,使得内存管理变得更加方便和安全。这些内存管理类可以自动进行内存释放,避免了手动释放内存的繁琐工作。值得一提的是,上面的AutoIntPtr
就是一个简化版的智能指针了。
在实际开发中,RAII的应用很广。不仅仅用于自动释放内存。还可以用来关闭文件、释放数据库连接、释放同步锁等。
本文介绍了C++中的内存管理机制,包括内存分区、栈、堆和RAII技术等内容。通过学习本文,我们可以更好地掌握C++的内存管理技巧,避免内存泄漏和内存溢出等问题。
51、学习使用按位与&。程序分析:0&0=0;0&1=0;1&0=0;1&1=1。参考代码:#include<stdio.h> intmain() { inta,b; a=077; b=a&3; printf("a&b(decimal)为%d\n",b); b&=7; printf("a&b(decimal)为%d\n",b); return0; } 复制运行结果:a&b(decimal)为3 a&b(decimal)为352、学习使用按位或|。程序分析:0|0=0;0|1=1;1|0=1;1|1=1。参考代码:#include<stdio.h> intmain() { inta,b; a=077; b=a|3; printf("b的值为%d\n",b); b|=7; printf("b的值为%d\n",b); return0; } 复制运行结果:b的值为63 b的值为6353、学习使用按位异或^
本人kaggle分享链接:https://www.kaggle.com/c/bengaliai-cv19/discussion/128592Mixupfromtorchtoolbox.toolsimportmixup_data,mixup_criterion alpha=0.2 fori,(data,labels)inenumerate(train_data): data=data.to(device,non_blocking=True) labels=labels.to(device,non_blocking=True) data,labels_a,labels_b,lam=mixup_data(data,labels,alpha) optimizer.zero_grad() outputs=model(data) loss=mixup_criterion(Loss,outputs,labels_a,labels_b,lam) loss.backward() optimizer.update()复制复制Cutoutfromtorchvisionimporttransforms
前言一.问题案例1案例2二.探究问题三.总结前言最近在突然想到了String字符串拼接问题,于是做了一个demo测试了一下,到底String类型的字符串在拼接的时候,哪种情况下会走会走StringBulider进行字符串拼接,而哪种情况编译器会对代码进行优化?话不多说,先看demo一.问题案例1测试代码1.png可以发现,str==str2的结果为false,那么我们在看看下一个例子。案例2测试代码2.png这时候,两个字符串对比的结果为true。二.探究问题这时候,疑问就来了,为什么结果会不一致呢?利用在cmd窗口输入javap-cTestDemo.class命令,对字节码文件进行反编译,发现了问题所在?测试代码1cmd.png可以看到在案例1中,java代码底层走了StringBuilder,进行字符串拼接,然后调用了StringBuilder的toString方法。测试代码2cmd.png而案例2中,对class文件进行反编译,发现代码出现了一点变化,并没有走StringBuilder进行字符串拼接。三.总结案例1中,通过变量和字符串拼接,java是需要先到内存找变量对应的值,才
一、内存操作和IO操作在计算机运行执行程序的世界里,从如何得到处理结果分成两大类:1.内存操作:CPU在内存里面完成计算,然后得到处理结果。2.IO操作:CPU会把内存中的程序委托给其他的网络、磁盘等驱动程序,让这些外部的驱动程序来进行具体的处理,处理完成以后再返回给内存程序。对于这两类操作的优化方式是不一样的。内存操作的特点是占用CPU资源,CPU不断的计算。对于内存密集型的操作(Compute-BoundOperation)的优化,我们可以把一个大任务拆分成多个互不影响的子任务,那么就能让多个CPU同时参与运算,最后合并子任务的结果,所花的时间自然就少了。所以内存密集型的操作(Compute-BoundOperation)的优化有一个前提:超线程、多核、甚至是真正的多个CPU的计算机能够同时运行多个线程,对于只有一个CPU的计算机不适合。多线程之间的状态切换是需要额外的CPU资源的。IO操作的特点是基本不占用CPU资源,但是它会占用当前的工作者线程,并使其进入等待状态,等待IO完成的处理结果,然后在继续执行。但是在ASP.NET这种天然多线程的环境里,CLR线程池容量是有上限的,这
『优秀论文精选』《TimeSeriesForecastingusingRNNs:anExtendedAttentionMechanismtoModelPeriodsandHandleMissingValues》链接:https://arxiv.org/pdf/1703.10089.pdf 『AI头条』《Google开源机器翻译项目》链接:https://github.com/tensorflow/nmt《ImitationfromObservation:LearningtoImitateBehaviorsfromRawVideoviaContextTranslation》(通过最新「模仿学习」技术为机器人「赋能」)YLiu,AGupta,PAbbeel,SLevine[UCBerkeley&OpenAI](2017)链接:https://sites.google.com/site/imitationfromobservation/『深度学习tips』《如何看待Face++出品的小型化网络ShuffleNet?》论文地址:https://arxiv.org/abs/1707.01
前言:前面已经为了这章做了一点铺垫了,关于vuex也有很多读者私信我关于这方面的问题,其实vuex也很简单,今天我们就来探探vuex。 GitHub:https://github.com/Ewall1106/mall(选择chapter33分支) 1、vuex基本概念Vuex是什么?Vuex是一个专为Vue.js应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。关于vuex的基本概念解释基本就是一张图的事情:截图来自vuex官网大家好好看看官网的解释。2、初探实践看完以后应该是有点懵逼的,所以这就是这篇文章存在的意义了:就是让你不那么懵逼;总归理论与实践相结合才能真正弄明白一个东西,所以我们简单实践一下。(1)安装vuex安装(2)然后就是新建一个store文件专门用于我们vuex的状态管理;在store文件夹下新建一个一个index.js引入vue、vuex并注册导出vuex定义了一个state对象,这个对象包含了全部应用层级状态(全局共用的数据)引入(3)然后我们还需要在main.js中注册这个vuex仓库注册这
近日,葡萄城成功签约赛捷软件(上海)有限公司(以下简称“赛捷软件”),ActiveReports.NET报表控件助力赛捷软件旗下各类型管理软件中各类数据报表应用。赛捷软件(上海)有限公司为赛捷集团拓展在华业务,赛捷集团是全球第三大管理软件及解决方案专业提供商,并在伦敦股票交易所上市(股票代码SGE),公司目前在全球拥有超过700万家企业用,在全球拥有超过13,400名员工,拥有超过3万家专注于各行业解决方案的合作伙伴,拥有超过700万家企业用户。赛捷集团2013财年销售收入达到21.7亿美金,是专为成长型企业提供全线管理软件解决方案以及相关产品和服务的国际领先供应商。在中国,赛捷软件超过19年的历史,致力于以世界级的优秀软件方案帮助中国成长型企业开展先进企业管理,超过2,000家企业用户在运行赛捷的管理软件,赛捷精细化的管理解决方案覆盖了ERP、CRM、HCM、BI、企业社交、项目管理等功能,并且针对行业特殊需求提供行业解决方案。同时在移动互联、大数据、物联网的发展趋势下,重磅推出移动互联系列解决方案,结合新营销的特性,为企业提供更多元化的服务,致力成为推动新领域发展的智慧企业。葡萄城
AiTechYun编辑:chux 谷歌的DeepMind今天分享了研究和实验的结果,其中多个人工智能系统经过训练,可以在QuakeIIIArena中玩夺旗(CapturetheFlag),这是一款多人第一人称射击游戏。在这个过程中受过训练的AI现在比游戏中的大多数人类玩家更好,无论它是与人类还是机器队友一起玩。名为FortheWin(FTW)的人工智能玩了近45万场QuakeIIIArena游戏,以获得其对人类玩家的统治地位,并理解如何有效地与其他机器和人类合作。DeepMind指的是训练多个独立的操作智能体,作为多代理学习的实践采取集体行动。该公司在博客文章中说:“我们训练能够学习并充当个人的智能体,但必须能够与任何其他智能体或人类进行团队合作。从多智能体的角度来看,夺旗需要成员成功地与队友合作并与对方球队竞争,同时保持他们可能遇到的任何比赛风格。”以前一些关于视频游戏和强化学习的研究主要集中在有少数玩家的环境中,而DeepMind的实验涉及30个智能体同时对抗四场比赛中的人类或机器。在与40名人类夺旗玩家的比赛中,机器的团队在对抗人类的比赛中获胜,并且有95%的机会赢得人类与机器合
摘自:驱动之家 网站:www.mydrivers.com25年前,也就是1990年2月14日,飞行中的旅行者1号,完成了有史以来的第一幅太阳系家族照。在一张组合照片中,太阳、金星、地球、木星、土星、天王星、海王星等汇聚一堂。你说水星和火星?其实它俩也在照片里的,但作为家族中最内围的成员,水星因为太靠近太阳而无法分辨出来,火星则不幸地淹没在相机光学系统所散射的阳光里,所以这个全家福有点遗憾。冥王星已经不属于大行星之列,所以不考虑它了。至于为什么整个照片都是灰色的,主要是因为旅行者1号用的广角镜头对着太阳拍照的时候,光线太多太亮,所以加了个灰色滤镜。现在基本可以确认,旅行者1号已经完全离开太阳系了,也是第一个进入星际空间的人造物体。它满载着地球上人类和生命的信息,将一直走向宇宙深处。它的旅程没有止尽,离开太阳系后要经历4万年才能到达下一个星系,可是到了2025年,它的科学仪器就会彻底没电了,2036年传输信号的电力也也会彻底耗尽,再也不能告诉我们什么了。不过在一个又一个的情人节里,它并不孤独,人类一直陪伴着它。@MangleKuo做的中文版哦对了,说起情人节,NASA也又浪漫了一把,大家
Ansible性能优化 在默认设置的情况下,Ansible的执行效率已经可以满足大多数场景。 面对巨量目标主机时,可以通过一些配置优化去再提高ansible的执行效率。 Ansible基本设置 #通过time命令统计执行时间 time<ansible-command> #关闭SSH密钥检测 -以SSH登录远程设备时,默认该设备会检查远程主机的公钥,并且将该公钥记录在~/.ssh/known_hosts文件中 -当下次该主机访问时会核对公钥,如果公钥不同则会发出警告,如果公钥相同,则会提示输入密码 -对主机公钥的检查是根据StrictHostKeyChecking变量的检查级别确定:no(不检查)、ask(是否检查要询问)、yes(每次都检查)、False(关闭检查) -设置/etc/ansible/ansible.cfg中参数host_key_checking=False #并发数forks -Ansible默认只会创建5个进程并发执行任务,所以在任务中只能同时控制5台主机执行 -forks是线程数,受限于CPU的核心数,加大forks值能让CPU尽量提升并发数量
原因:这是新版webpack存在的BUG,卸载现有的新版本webpack,装老版本就好 webpack-dev-server版本需要从最新版本降低到如下版本,因为开始构建项目所用的插件版本太低 (1)、npmuninstallwebpack-dev-server (2)、npminstallwebpack-dev-server@2.9.1 (3)、npmrundev =================== 如何执行npmuninstallwebpack-dev-server也报错的话只能用下面方法了 原因是你的node_modules有意外改动,导致依赖库不完整。 解决: 1.删除项目下的node_modules 2.在项目目录下重新执行npminstall或者cnpminstall,会重新生成node_modules 3.执行npmrunbuild或者cnpmrunbuild 4.执行npmrundev或者cnpmrundev ================== PSE:\webcode\bigbullmobile>cnpmrundev >bigbu
网页的图片大致是用Image导入的,使用的是相对路径,例如 <imagesrc="image/bg.jpg"/>复制 通过匹配可以获取image/bg.jpg,与页面地址组合可以得到图片的地址 除了直接引入的图片,还有通过CSS,HTML引入的图片,也需要处理 #-*-coding:utf-8-*- importurllib,httplib,urlparse importsys importre defhttpExists(url): host,path=urlparse.urlsplit(url)[1:3] if':'inhost: #portspecified,trytouseit host,port=host.split(':',1) try: port=int(port) exceptValueError: print'invalidportnumber%r'%(port,) returnFalse else: #noportspecified,usedefaultport port=None try: connection=httplib.HTTPConn
Overview|SDUTOnlineJudge 动态规划: 1.确定dp数组的含义 2.找到递推表达式 3.确定边界值,左边初始化,那一定左退右,上边初始化,一定上推下 动态规划是1生2,2生3的问题,后一个结果一定与前一个或前几个结果有关系 A-递归的函数 最简单的形式,只是保存了历史记录 1#include<iostream> 2usingnamespacestd; 3intp[21][21][21]={0}; 4intf(inta,intb,intc) 5{ 6if(a<=0||b<=0||c<=0) 7return1; 8elseif(a>20||b>20||c>20) 9{ 10if(p[20][20][20]==0) 11p[20][20][20]=f(20,20,20); 12returnp[20][20][20]; 13} 14elseif(a<b&&b<c) 15{ 16if(p[a][b][
TimeLimit:10000ms CaseTimeLimit:1000ms MemoryLimit:256MB Description 小Hi家的阳台上摆着一排N个空花盆,编号1~N。从第一天开始,小Hi每天会选择其中一个还空着花盆,栽种一株月季花,直到N个花盆都栽种满月季。 我们知道小Hi每天选择的花盆的编号依次是A1,A2,...AN。随着花盆中被栽种上月季,连续的空花盆数量越来越少。 现在小Ho想知道,第一次出现恰好K个连续空花盆(恰好是指这K个空花盆两边相邻的位置都不是空花盆)是第几天? 假设N=7,K=2,小Hi第1天~第7天选择的花盆编号依次是:4、2、7、5、1、3、6。 1234567 OOOOOOO第0天,一段7个连续空花盆 OOOXOOO第1天,一段3个连续空花盆,和另一段3连续个空花盆 OXOXOOO第2天,1个、1个和3个连续空花盆 OXOXOOX第3天,第一次出现2个连续空花盆 ....复制 Input 第一行包含两个整数,N和K。 第二行包含N个两两不同的整数,A1,A2,...AN。 对于30%的数据,
41道题: 重要的 连表查询 分组 制表语句: 班级表 Table:class CreateTable:CREATETABLE`class`( `cid`int(11)NOTNULLAUTO_INCREMENT, `caption`varchar(50)DEFAULTNULL, PRIMARYKEY(`cid`) )ENGINE=InnoDBAUTO_INCREMENT=4DEFAULTCHARSET=utf8 1rowinset(0.00sec) 学生表: Table:student CreateTable:CREATETABLE`student`( `sid`int(11)NOTNULLAUTO_INCREMENT, `sname`varchar(50)DEFAULTNULL, `gender`char(10)DEFAULTNULL, `class_id`int(11)DEFAULTNULL, PRIMARYKEY(`sid`), KEY`fk_student_class`(`class_id`), CONSTRAINT`fk_student_class`FOREIGNK
微信小程序登录方案 登录程序app.js 调用wx.login获取code 将code作为参数请求自己业务登录接口获取session_key 存储session_key 如果有回调执行回调 App({ wxLogin:function(callback){ wx.login({ success:function(res){ console.log("weixinres",res); if(res.code){ //发起网络请求 wx.request({ url:`${app.globalData.apiurl}wx_login`, data:{ code:res.code }, success:function(data){ console.log("请求登录接口返回",data); wx.setStorage({ key:"session_key", data:data.data.data.session_key }); if(callback&&typeof(callback)==="function"){ callback(data.data.sessio
EasyAR提供一个灵活的target管理接口,简单易用。你可以在运行时生成EasyAR的target,无需登录网站上传下载很多数据。 EasyAR通过两个步骤来管理target。 第一步是加载配置来设置target。 第二步是将target加载进入tracker中用以跟踪。EasyAR通过异步方式加载和卸载target到tracker,不会阻塞调用线程,因而可以动态增量加载。 Json配置 EasyAR使用json格式来存储target的配置。完整的数据集接口示例如下 { "images": [ { "image":"idback.jpg", "name":"idback", "size":[8.56,5.4], "uid":"uid-string" } ] } 复制 上面的json包含了所有可以被EasyAR使用的配置项:target图像路径、target名字、target大小以及target的uid。这里面只有target图像路径是必需的。所以下面这个json也是可接受的。 { "images": [ { "image":"argame00.jpg", "nam
GDS-50型时间检定仪是完全依据JJG237-2010《秒表检定规程》进行设计研发的一款秒表检定设备,现在已在市场上达到70%的占有率,本文主要对时间检定仪的特点进行总结,方便用户在选择时更加清晰的确认可实现性和操作性能。 1、时间检定仪的功能 JJG237-2010《秒表检定规程》中列举了时间检定仪在对秒表检定时需要具备的四种仪器功能有: a. 秒表检定仪功能:用于检定电子秒表和机械秒表; b. 日差测量仪或校表仪功能:用于快速测量电子秒表的日差; c. 指针式电秒表检定仪功能:用于检定指针式电秒表的固有误差 d. 标准时间间隔发生器功能:用于检定数字式电秒表 GDS-50型时间检定仪是根据JJG237-2010《秒表检定规程》的要求制作的一款多功能,综合性的时间检定自动测试装置,根据检规的要求,设计了检规中要求的四种仪器功能要求区域,产品设计细节如下图所示: 如图标示,时间检定仪结合了秒表检定仪、日差测量仪/校表仪、指针式电秒表检定仪、标准时间间隔发生器等4种功能,采用高稳定度石
问卷调查 1.你对自己的未来有什么规划?做了哪些准备? ---未来的规划,以前想做生意,想做医生,但是好像这个专业都不行,所以只能尽力地去学习这个专业的一切 2.你认为什么是学习?学习有什么用?现在学习动力如何?为什么? ---学习自己想要的知识并用到,一定会用到的。。以后工作,当然最重要的是用到做人。。至于动力,真的没什么动力,只是单纯地不想输别人,至于专业没什么兴趣,但是不想就这么颓废下去,要培养出来。 3.你感觉自己什么事情做的比较成功?有什么经验? ---比较成功的在我看来就是没想其他人一样选错专业却放弃专业了。经验就是不要输,一定要赢,既然选了,就没时间去后悔了。 4.你怎么看待软件工程这个专业?学习这个专业你对自己有什么期望? ---这个专业从深入下去才知道真的计算机世界很奇妙,除了他不认识我,我很难认识他之外,还是有可取之处的。而以后我希望就用这个代码,实现我的梦想。 5.你是怎么学习C语言的?(作业,实验,教材,其他),目前为止估算自己写过多少行代码? ---学习c就是老师讲的,图书
ELF(executableandlinkformat文件里面包含了符号表,汇编等。BIN文件是将elf文件中的代码段,数据段,还有一些自定义的段抽取出来做成的一个内存的镜像。 1.进入在项目的property设置2.进入C/C++Build内的setting项3.打开BuildSteps标签,在Postbuild项中添加以下命令行 arm-atollic-eabi-objcopy-Obinaryyourfilename.elfyourfilename.bin或者arm-atollic-eabi-objcopy.exe-Obinary ${BuildArtifactFileBaseName}.elf ${BuildArtifactFileBaseName}.bin 生成Hex:arm-atollic-eabi-objcopy.exe-Oihex ${BuildArtifactFileBaseName}.elf ${BuildArtifactFileBaseName}.hex 如下图: