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

C++中的动态内存管理

1.C++中动态内存管理

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

1.1 new/delete操作内置类型

  • c语言和c++的动态内存开辟对比
//c语言和c++的动态内存开辟对比
int main()
{//C语言的动态内存开辟int* p1 = (int*)malloc(sizeof(int);if (p1 == NULL){perror("malloc");}//c++动态的内存开辟int* p2 = new int;//不会初始化int* p3 = new int(10);//初始化,一个intint* p4 = new int[10];//开辟10个int型的空间int* p5 = new int[10]{ 1,2,3,4,5, };//开辟10个int型的空间并初始化为{1,2,3,4,5,0,0,0,0,0,0}free(p1);delete p2;delete p3;delete[] p3;delete[] p4;return 0;
}

image-20240327061718502.png

注意:申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用new[]和delete[],注意:匹配起来使用。

  • malloc 和free ,new和delete,new [],delete[] 都要配对使用,如果交叉使用可能会有奇怪的问题。

1.2 new和delete操作自定义类型

  • C语言中动态内存开辟和c++中动态内存开辟的对比,当然c++兼容c的动态内存开辟。
class A
{
public:A(int a = 0): _a(a){cout << "A():" << this << endl;}~A(){cout << "~A():" << this << endl;}
private:int _a;
};struct ListNode
{int _val;ListNode* _next;ListNode(int val):_val(val), _next(nullptr){}
};//ListNode BuyListNode(int x)
//{
//	//...
//}int main()
{// new/delete 和 malloc/free最大区别是 new/delete对于【自定义类型】除了开空间还会调用构造函数和析构函数A* p1 = (A*)malloc(sizeof(A));A* p2 = new A(1);free(p1);delete p2;// 内置类型是几乎是一样的int* p3 = (int*)malloc(sizeof(int)); // Cint* p4 = new int;
free(p3);
delete p4;A* p5 = (A*)malloc(sizeof(A)*10);A* p6 = new A[10];free(p5);delete[] p6;//与节点的对比,与c语言中ListNode* n1 = new ListNode(1);ListNode* n2 = new ListNode(2);ListNode* n2 = new ListNode(3);return 0;
}

注意:在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,而malloc与free不会

2. operator new与operator delete函数

2.1 operator new与operator delete函数(重点)

new和delete是用户进行动态内存申请和释放的操作符operator new 和operator delete

系统提供的全局函数new在底层调用operator new全局函数来申请空间,delete在底层通过

operator delete全局函数来释放空间.

  • operator new是个函数名,与运算符重载没有关系。
  • c++和c的内存泄漏都不会报错,都是自己管理的,要求你自己释放。java 有垃圾回收,在虚拟机实现,对象用完会自动释放,但是对性能有影响。
  • new的底层是operator new ,operator new的底层是malloc,delete同理。
/*
operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间
失败,尝试执行空间不足应对措施,如果改应对措施用户设置了,则继续申请,否则抛异常。
*/
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
// try to allocate size bytes
void *p;
while ((p = malloc(size)) == 0)if (_callnewh(size) == 0){// report no memory// 如果申请内存失败了,这里会抛出bad_alloc 类型异常static const std::bad_alloc nomem;_RAISE(nomem);}
return (p);
}
/*
operator delete: 该函数最终是通过free来释放空间的
*/
void operator delete(void *pUserData)
{_CrtMemBlockHeader * pHead;RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));if (pUserData == NULL)return;_mlock(_HEAP_LOCK);  /* block other threads */__TRY/* get a pointer to memory block header */pHead = pHdr(pUserData);/* verify block type */_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));_free_dbg( pUserData, pHead->nBlockUse );__FINALLY_munlock(_HEAP_LOCK);  /* release other threads */__END_TRY_FINALLYreturn;
}
/*
free的实现
*/
#define   free(p)               _free_dbg(p, _NORMAL_BLOCK)

通过上述两个全局函数的实现知道,operator new 实际也是通过malloc来申请空间,如果malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施就继续申请,否则就抛异常。operator delete 最终是通过free来释放空间的

  • c语言和C++开辟空间的对比,new和malloc ,free和delete之间的对比
class Stack
{
public:Stack(){cout << "Stack()" << endl;_a = new int[4];_top = 0;_capacity = 4;}~Stack(){cout << "~Stack()" << endl;delete[] _a;_top = _capacity = 0;}private:int* _a;int _top;int _capacity;
};class A
{};
int main()
{int* p1 = (int*)operator new(sizeof(int));//开辟失败会抛出异常int* p2 = (int*)malloc(sizeof(int));//开辟失败返回nullptrif (p2 == nullptr){perror("malloc");}//申请空间-》调用构造函数//封装malloc到operator new,开辟失败抛出异常,所以new的底层还是mlloc只不过封装了一些异常,保持面向对象的特性A* p3 = new A;//先调用析构函数-》再释放p5的空间//operator deletedelete p3;//operator new[] 调用了operator new,一样的A* p4 = new A[10];//调用10次构造函数//operator delete[] p4指向的空间。delete[] p4;//调用10次析构函数//这种不匹配不会报错,都是内置类型int* p5 = new int[10];free(p5);//这种不匹配不会报错,A没有资源释放A* p6 = new A;free(p6);//没有调用析构函数,但是类里面没有需要释放的内存,所以不会内存泄漏Stack st;//不用释放内存,因为这是自定义类型,生命周期结束会自动调用析构函数Stack* pst = new Stack;//delete pst;//先调用析构函数,再去释放pst指向的空间//free(pst);//只释放了pst指向的空间,没有调用析构函数释放类里面开辟的空间,而且c/c++都不会报错A* p7 = new A[10];//delete p7;//这个只会释放一个A的空间delete[] p7;//free(p7);//不会报错,会释放所有空间(这里可能有点问题,用到再看看)return 0;
}
  • delete调用析构函数的流程 和 自定义类型直接结束自己调用析构

image-20240330060359929.png

如果只delete一个A对象是从p9开始释放一个对象,但是如果有[],它就会减一个位置,然后知道要释放10个对象,仅限vs编译器是这样

image-20240330060547444.png

3.new和delete的实现原理

3.1 内置类型

如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛异常,malloc会返回NULL。

3.2 自定义类型

  • new的原理
  1. 调用operator new函数申请空间

  2. 在申请的空间上执行构造函数,完成对象的构造

  • delete的原理
  1. 在空间上执行析构函数,完成对象中资源的清理工作

  2. 调用operator delete函数释放对象的空间

  • **new T[N]**的原理
  1. 调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请

  2. 在申请的空间上执行N次构造函数

  • delete[]的原理
  1. 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理

  2. 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间

4. 定位new表达式(placement-new) (了解)

定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象

使用格式:

new (place_address) type或者new (place_address) type(initializer-list)place_address必须是一个指针,initializer-list是类型的初始化列表

使用场景:

定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如果是自定义类型的对象,需要使用new的定义表达式进行显示调构造函数进行初始化。

class A
{
public:A(int a = 0): _a(a){cout << "A():" << this << endl;}~A(){cout << "~A():" << this << endl;}
private:int _a;
};
// 定位new/replacement new
int main()
{// p1现在指向的只不过是与A对象相同大小的一段空间,还不能算是一个对象,因为构造函数没有执行A* p1 = (A*)malloc(sizeof(A));new(p1)A;  // 注意:如果A类的构造函数有参数时,此处需要传参//对一块已有的空间初始化 --- 定位new//new(p1)A;p1->~A();free(p1);A* p2 = (A*)operator new(sizeof(A));new(p2)A(10);p2->~A();operator delete(p2);return 0;
}
class A
{
public:A(){}A(int a){}
};
int main()
{A* p1 = (A*)malloc(sizeof(A));if (p1 == nullptr){perror("malloc");}//对一块已有的空间初始化 --- 定位new//new(p1)A;new(p1)A(1);return 0;A* p2 = new A;p1->~A();free(p1);delete p2;
}

程序需要频繁使用内存,就提出使用内存池的机制。

向操作系统的堆申请空间比较慢,因为操作系统所有的都向这里申请,如果想要快点,就建立一个内存池,有位置就申请,无位置就继续返回操作系统申请

image-20240330095144501.png

  • 以下是关于C/C++内存分布的更多信息和示例:
  1. 多线程与内存分布:在多线程环境中,每个线程都有自己的栈区,用于存储线程执行过程中的现场信息。而堆区和全局变量区则被所有线程共享。因此,在多线程编程中,需要注意同步和互斥机制的使用,以避免因多个线程同时访问同一块内存而导致的数据竞争。

示例:

#include <iostream>
#include <thread>void thread_func() {std::cout << "Thread ID: " << std::this_thread::get_id() << '\n';
}int main() {std::thread t1(thread_func);std::thread t2(thread_func);t1.join();t2.join();return 0;
}
  1. 内存映射文件:内存映射文件是一种将磁盘文件映射到内存空间的技术。通过内存映射文件,程序可以直接访问文件内容,而无需通过传统的文件读写操作。这种技术可以提高程序的性能,简化文件操作。

示例:

#include <iostream>
#include <fstream>
#include <sys/mman.h>
#include <fcntl.h>int main() {const char* filename = "example.txt";int fd = open(filename, O_RDONLY);void* map_ptr = mmap(NULL, sizeof(int), PROT_READ, MAP_PRIVATE, fd, 0);int value = *(int*)map_ptr;std::cout << "Value from file: " << value << '\n';munmap(map_ptr, sizeof(int));close(fd);return 0;
}

以上示例和信息展示了C/C++内存分布的更多应用场景和技巧。如有更多问题,请随时提问。

相关文章:

C++中的动态内存管理

1.C中动态内存管理 C语言内存管理方式在C中可以继续使用&#xff0c;但有些地方就无能为力&#xff0c;而且使用起来比较麻烦&#xff0c;因此C又提出了自己的内存管理方式&#xff1a;通过new和delete操作符进行动态内存管理。 1.1 new/delete操作内置类型 c语言和c的动态内存…...

es6的核心语法

在学习低代码时&#xff0c;经常有粉丝会问&#xff0c;低代码需要什么基础&#xff0c;es6就是基础中的一项。我们本篇是做一个扫盲&#xff0c;可以让你对基础有一个概要性的了解&#xff0c;具体的每个知识点可以深入进行了解&#xff0c;再结合官方模板就会有一个不错的掌握…...

Unity | 射线检测及EventSystem总结

目录 一、知识概述 1.Input.mousePosition 2.Camera.ScreenToWorldPoint 3.Camera.ScreenPointToRay 4.Physics2D.Raycast 二、射线相关 1.3D&#xff08;包括UI&#xff09;、射线与ScreenPointToRay 2.3D&#xff08;包括UI&#xff09;、射线与ScreenToWorldPoint …...

职业经验 2024 年测试求职手册

原贴地址: 2024 年测试求职手册 TesterHome 经历年前年后差不多 2 个月左右时候的求职&#xff0c;是时候总结复盘一下了&#xff0c;本打算在自己有着落再复盘&#xff0c;但是一想那时候似乎价值就没现在去做显得有意义一些&#xff0c;这篇帖子更多的是让大家看下有没有心…...

Spring Boot与Redis深度整合:实战指南

Spring Boot 整合 Redis 相当简单&#xff0c;它利用了 Spring Data Redis 项目&#xff0c;使得我们可以在 Spring Boot 应用中轻松地操作 Redis。以下是如何整合 Redis 到 Spring Boot 应用的基本步骤&#xff1a; 1. 添加依赖 首先&#xff0c;在你的 pom.xml 文件中添加 …...

微服务(基础篇-006-Docker安装-CentOS7)

目录 05-初识Docker-Docker的安装_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1LQ4y127n4?p46&spm_id_frompageDriver&vd_source60a35a11f813c6dff0b76089e5e138cc 0.安装Docker 1.CentOS安装Docker 1.1.卸载&#xff08;可选&#xff09; 1.2.安装dock…...

前端-css-01

1.CSS 长度单位和颜色设置 1.1CSS 中的长度单位 px 像素 em 字体大小的倍数&#xff08;字体默认是16px&#xff09; % 百分比 1.2CSS 中的颜色设置方式 1.2.1使用颜色名表示颜色 red、orange、yellow、green、cyan、blue、purple、pink、deeppink、skyblue、greenyellow .…...

Java学习36-Java 多线程安全:懒汉式和饿汉式

JAVA种有两种保证线程安全的方式&#xff0c;分别叫懒汉式Lazy Initialization和饿汉式Eager Initialization&#xff0c;以下是他们的区别&#xff1a; 线程安全性&#xff1a; 懒汉式本身是非线程安全的&#xff0c;因为多个线程可能同时检查实例是否为null&#xff0c;并尝…...

sql常用之CASE WHEN THEN

sql常用之CASE WHEN THEN SQL中的 CASE 类似编程语言里的 if-then-else 语句&#xff0c;用做逻辑判断。可以用于SELECT语句中&#xff0c;也可以用在WHERE&#xff0c;GROUP BY 和 ORDER BY 子句&#xff1b;可以单独使用&#xff0c;也可以和聚合函数结合使用。 语法&#…...

【PduR路由】IPduM模块详细介绍

目录 1.IpduM功能简介 2.IpduM模块依赖的其他模块 2.1RTE (BSW Scheduler) 2.2PDU Router 2.3COM 3.IpduM功能详解 3.1 功能概述 3.2 I-PDU多路复用I-PDU Multiplexing 3.2.1 Definitions and Layout 3.2.2通用功能描述 General 3.2.3模块初始化 Initialization 3.…...

【MySQL】6.MySQL主从复制和读写分离

主从复制 主从复制与读写分离 通常数据库的读/写都在同一个数据库服务器中进行&#xff1b; 但这样在安全性、高可用性和高并发等各个方面无法满足生产环境的实际需求&#xff1b; 因此&#xff0c;通过主从复制的方式同步数据&#xff0c;再通过读写分离提升数据库的并发负载…...

Lucene及概念介绍

Lucene及概念介绍 基础概念倒排索引索引合并分析查询语句的构成 基础概念 Document&#xff1a;我们一次查询或更新的载体&#xff0c;对比于实体类 Field&#xff1a;字段&#xff0c;是key-value格式的数据&#xff0c;对比实体类的字段 Item&#xff1a;一个单词&#xff0…...

密码算法概论

基本概念 什么是密码学&#xff1f; 简单来说&#xff0c;密码学就是研究编制密码和破译密码的技术科学 例题&#xff1a; 密码学的三个阶段 古代到1949年&#xff1a;具有艺术性的科学1949到1975年&#xff1a;IBM制定了加密标准DES1976至今&#xff1a;1976年开创了公钥密…...

实时数仓之实时数仓架构(Hudi)

目前比较流行的实时数仓架构有两类&#xff0c;其中一类是以FlinkDoris为核心的实时数仓架构方案&#xff1b;另一类是以湖仓一体架构为核心的实时数仓架构方案。本文针对FlinkHudi湖仓一体架构进行介绍&#xff0c;这套架构的特点是可以基于一套数据完全实现Lambda架构。实时数…...

2022-04-15_for循环等_作业

for循环 编写程序数一下 1到 100 的所有整数中出现多少个数字9计算1/1-1/21/3-1/41/5 …… 1/99 - 1/100 的值&#xff0c;打印出结果求10 个整数中最大值在屏幕上输出9*9乘法口诀表二分查找 编写程序数一下 1到 100 的所有整数中出现多少个数字9 #include <stdio.h>in…...

脑机辅助推导算法

目录 一&#xff0c;背景 二&#xff0c;华容道中道 1&#xff0c;问题 2&#xff0c;告诉脑机如何编码一个正方形格子 3&#xff0c;让脑机汇总信息 4&#xff0c;观察图&#xff0c;得到启发式算法 5&#xff0c;根据启发式算法求出具体解 6&#xff0c;可视化 一&am…...

【原创教程】三菱FX PLC控制FR-E740变频器

变频器的使用 1. 使用三菱FX PLC 控制变频器时,接线图请按下图所示接线。 各个端子的说明如下: R、S、T:变频器电源,E740变频器电源位3相380V。 STF:正转启动, STF信号ON时为正转、OFF时为停止指令。 STR :反转启动,STR信号ON时为反转、OFF时为停止指令。 RH、RM、RL…...

重读Java设计模式: 深入探讨建造者模式,构建复杂对象的优雅解决方案

引言 在软件开发中&#xff0c;有时需要构建具有复杂结构的对象&#xff0c;如果直接使用构造函数或者 setter 方法逐个设置对象的属性&#xff0c;会导致代码变得冗长、难以维护&#xff0c;并且容易出错。为了解决这个问题&#xff0c;我们可以使用建造者模式。 一、建造者…...

C语言数据结构易错知识点(6)(快速排序、归并排序、计数排序)

快速排序属于交换排序&#xff0c;交换排序还有冒泡排序&#xff0c;这个太简单了&#xff0c;这里就不再讲解。 归并排序和快速排序都是采用分治法实现的排序&#xff0c;理解它们对分支思想的感悟会更深。 计数排序属于非比较排序&#xff0c;在数据集中的情况下可以考虑使…...

使用 React Router v6.22 进行导航

使用 React Router v6.22 进行导航 React Router v6.22 是 React 应用程序中最常用的路由库之一&#xff0c;提供了强大的导航功能。本文将介绍如何在 React 应用程序中使用 React Router v6.22 进行导航。 安装 React Router 首先&#xff0c;我们需要安装 React Router v6…...

单链表的插入和删除

一、插入操作 按位序插入&#xff08;带头结点&#xff09;&#xff1a; ListInsert(&L,i,e):插入操作。在表L中的第i个位置上插入指定元素e。 typedef struct LNode{ElemType data;struct LNode *next; }LNode,*LinkList;//在第i 个位置插插入元素e (带头结点) bool Li…...

全量知识系统 之“程序”详细设计 之 “絮”---开端“元素周期表”表示的一个“打地鼠”游戏

全量知识系统 之“程序”详细设计 概述-概要和纪要 序 絮&#xff08;一个极简的开场白--“全量知识系统”自我介绍&#xff09; 将整个“人生”的三个阶段 比作“幼稚园”三班 &#xff1a; 第一步【想】-- “感性”思维游戏&#xff1a;打地鼠 。学前教育-新生期&#x…...

【详细讲解WebView的使用与后退键处理】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…...

【Linux多线程】生产者消费者模型

【Linux多线程】生产者消费者模型 目录 【Linux多线程】生产者消费者模型生产者消费者模型为何要使用生产者消费者模型生产者消费者的三种关系生产者消费者模型优点基于BlockingQueue的生产者消费者模型C queue模拟阻塞队列的生产消费模型 伪唤醒情况&#xff08;多生产多消费的…...

Django屏蔽Server响应头信息

一、背景 最近我们被安全部门的漏洞扫描工具扫出了一个服务端口的漏洞。这个服务本身是一个Django启动的web服务&#xff0c;并且除了登录页面&#xff0c;其它页面或者接口都需要进行登录授权才能进行访问。 漏洞扫描信息和提示修复信息如下: 自然这些漏洞如何修复&#xff0c…...

前端对数据进行分组和计数处理

js对数组数据的处理&#xff0c;添加属性&#xff0c;合并表格数据。 let data[{id:1,group_id:111},{id:2,group_id:111},{id:3,group_id:111},{id:4,group_id:222},{id:5,group_id:222} ]let tempDatadata; tempDatatempData.reduce((arr,item)>{let findarr.find(i>i…...

synchronized 和 lock

synchronized 和 Lock 都是 Java 中用于实现线程同步的机制&#xff0c;它们都可以保证线程安全。 # synchronized 介绍与使用 synchronized 可用来修饰普通方法、静态方法和代码块&#xff0c;当一个线程访问一个被 synchronized 修饰的方法或者代码块时&#xff0c;会自动获…...

ssh 公私钥(github)

一、生成ssh公私钥 生成自定义名称的SSH公钥和私钥对&#xff0c;需要使用ssh-keygen命令&#xff0c;这是大多数Linux和Unix系统自带的标准工具。下面&#xff0c;简单展示如何使用ssh-keygen命令来生成具有自定义名称的SSH密钥对。 步骤 1: 打开终端 首先&#xff0c;打开我…...

LangChain入门:8.打造自动生成广告文案的应用程序

在这篇技术博文中,我们将探讨如何利用LangChain框架的模板管理、变量提取和检查、模型切换以及输出解析等优势,打造一个自动生成广告文案的应用程序。 LangChain框架的优势 在介绍应用程序之前,让我们先了解一下LangChain框架的几个优势: 模板管理: 在大型项目中,文案可…...

AI如何影响装饰器模式与组合模式的选择与应用

​&#x1f308; 个人主页&#xff1a;danci_ &#x1f525; 系列专栏&#xff1a;《设计模式》《MYSQL应用》 &#x1f4aa;&#x1f3fb; 制定明确可量化的目标&#xff0c;坚持默默的做事。 &#x1f680; 转载自热榜文章&#xff1a;设计模式深度解析&#xff1a;AI如何影响…...

温州网站建设温州网站制作/百度网站域名

本节继续演示线条在排版中的作用,您将在本节使用线条描述内容的层级关系,同时也能起到引导观众视线的作用。 首先打开形状面板,并选择肘形箭头连接符工具。 在此处按下并向右上方拖动,以绘制一个肘形连接符。 然后将肘形连接符修改为虚线样式。...

包包网站建设策划书/什么是网络推广员

人生苦短&#xff0c;我选Python 前文传送门 小白学 Python&#xff08;1&#xff09;&#xff1a;开篇 小白学 Python&#xff08;2&#xff09;&#xff1a;基础数据类型&#xff08;上&#xff09; 小白学 Python&#xff08;3&#xff09;&#xff1a;基础数据类型&…...

如何在网站上做咨询浮动窗口/关键词排名代发

java课程设计_通讯录_通讯簿.doc还剩45页未读&#xff0c;继续阅读下载文档到电脑&#xff0c;马上远离加班熬夜&#xff01;亲&#xff0c;很抱歉&#xff0c;此页已超出免费预览范围啦&#xff01;如果喜欢就下载吧&#xff0c;价低环保&#xff01;内容要点&#xff1a;impo…...

网站鼠标移上去显示层/大数据营销名词解释

1、设置enable密码 R5(config)#enable password cisco 2、开启telnet&#xff0c;并设置telnet密码 R5#conf t R5(config)#line vty 0 4R5(config-line)#password ciscoR5(config-line)#loginR5(config-line)#transport input telnet 也可以在远程登入的时候设置不需要要密码&a…...

wordpress 功能开发教程/游戏推广论坛

定义一个方法的时候可以使用throws关键字声明。使用throws关键字声明的方法表示此方法不处理异常&#xff0c;而交给方法调用处进行处理。 throws关键字格式&#xff1a; public 返回值类型 方法名称&#xff08;参数列表&#xff0c;&#xff0c;&#xff0c;&#xff09;th…...

做淘宝客网站php/百度网盘电脑版下载

NAnt 是一个 Visual Studio .Net 应用程序的连编工具&#xff0c;对大而负责的工程而言&#xff0c;使用 NAnt 很方便。 1. 安装 从 http://nant.sourceforge.net 上可以下载源代码或者编译好的二进制文件&#xff0c;一般下载 nant-bin.zip &#xff0c;解压&#xff0c;…...