2024-06-23 编译原理实验4——中间代码生成

文章目录

    • 一、实验要求
    • 二、实验设计
    • 三、实验结果
    • 四、附完整代码

补录与分享本科实验,以示纪念。

一、实验要求

在词法分析、语法分析和语义分析程序的基础上,将C−−源代码翻译为中间代码。
要求将中间代码输出成线性结构(三地址代码),使用虚拟机小程序(附录B)来测试中间代码的运行结果。
由于在后面的实验中还会用到本次实验已经写好的代码,因此保持一个良好的代码风格、系统地设计代码结构和各模块之间的接口对于整个实验来讲相当重要!

  • 基本要求
    1. 对于正确的测试样例,输出可以在虚拟机上运行的中间代码文件。
    2. 在程序可以生成正确的中间代码(“正确”是指该中间代码在虚拟机小程序上运行结果正确)的前提下,效率也是最终评分的关键因素。
  • 附加要求
  1. 修改前面对C−−源代码的假设2和3,使源代码中:
    a) 可以出现结构体类型的变量(但不会有结构体变量之间直接赋值)。
    b) 结构体类型的变量可以作为函数的参数(但函数不会返回结构体类型的值)。
  2. 修改前面对C−−源代码的假设2和3,使源代码中:
    c) 一维数组类型的变量可以作为函数参数(但函数不会返回一维数组类型的值)。
    d) 可以出现高维数组类型的变量(但高维数组类型的变量不会作为函数的参数或返回类值)。

本实验只实现了abc,没有实现d。

二、实验设计

1、translate
该函数使用switch-case寻找需要处理的节点进行处理,否则继续寻找下层结点:

void translate(Node *h) {if (h == NULL) return;switch (h->type) {case Specifier:return;case FunDec: {translateFunDec(h);break;}case Dec: {translateDec(h);break;}case Stmt: {translateStmt(h);break;}default: {for (int i = 0; i < h->childNum; i++)translate(h->child[i]);break;}}
}

语义分析需要对每个匹配的产生式进行分析,因此需要写专门的函数进行操作。每个函数的操作均不一样,但也拥有相同的操作:

bool checkNode(Node *node, Types type) {if (node == NULL) {addLogNullNode(type);return false;}else if (node->type != type) {addLogTypeDismatch(type);return false;}addLogStartDealing(type);return true;
}bool hasBrothers(Node *node, int n, ...) {va_list vaList;va_start(vaList, n);Types next;for (int i = 0; i < n; ++i) {next = va_arg(vaList, Types);if (node == NULL || next != node->type) return false;node = node->brother;}return node == NULL;
}

2、translateExp
我们注意到,一个exp的处理需要分两种情况:刚定义和已定义。即未被处理和已被处理过。于是我们添加了一个参数option:option = 0表示exp未被用过,option = 1表示exp已被用过。
这样做的另一个好处是,可以实现exp值的向上传递。
因此,translateExp函数对每一个匹配的产生式都进行了两种处理:option = 0和option = 1。

void translateExp(Node *h, char *place, int option) {if (h->child[0]->type == _LP) {return translateExp(h->child[1], place, option);} else if (h->childNum == 3 && h->child[1]->type == _ASSIGNOP) {char temp[MAX_LENGTH];translateExp(h->child[0], temp, 0);translateExp(h->child[2], temp, 1);if (place != NULL) {if (option == 0) {strcpy(place, temp);} else {addIrCode(3, place, ":=", temp);}}} else if (h->childNum == 3 && h->child[1]->type == _LP) {if (place == NULL) {if (strcmp(h->child[0]->name, "read") == 0)return;char temp[MAX_LENGTH];newTemp(temp);addIrCode(4, temp, ":=", "CALL", h->child[0]->name);} else {if (option == 0)newTemp(place);if (strcmp(h->child[0]->name, "read") == 0)addIrCode(2, "READ", place);elseaddIrCode(4, place, ":=", "CALL", h->child[0]->name);}} else if (h->childNum == 4 && h->child[1]->type == _LP) {if (place == NULL) {if (strcmp(h->child[0]->name, "write") == 0) {char temp[MAX_LENGTH];translateExp(h->child[2]->child[0], temp, 0);addIrCode(2, "WRITE", temp);} else {FunDefUnit *f = getFunction(h->child[0]->name);translateArgs(h->child[2], f->parameterList, 0);char temp[MAX_LENGTH];newTemp(temp);addIrCode(4, temp, ":=", "CALL", h->child[0]->name);}} else {if (strcmp(h->child[0]->name, "write") == 0) {char temp[MAX_LENGTH];translateExp(h->child[2]->child[0], temp, 0);addIrCode(2, "WRITE", temp);if (option == 0) {place[0] = '#';place[1] = '0';place[2] = '\0';} else {addIrCode(3, place, ":=", "#0");}} else {FunDefUnit *f = getFunction(h->child[0]->name);translateArgs(h->child[2], f->parameterList, 0);if (option == 0)newTemp(place);addIrCode(4, place, ":=", "CALL", h->child[0]->name);}}}if (place == NULL)return;if (h->child[0]->type == _INT) {if (option == 0) {sprintf(place, "#%d", h->child[0]->intValue);} else {char temp[MAX_LENGTH];sprintf(temp, "#%d", h->child[0]->intValue);addIrCode(3, place, ":=", temp);}} else if (h->child[0]->type == _ID && h->childNum == 1) {if (option == 0) {strcpy(place, h->child[0]->name);} else {addIrCode(3, place, ":=", h->child[0]->name);}} else if (h->childNum == 3 &&(h->child[1]->type == _PLUS || h->child[1]->type == _MINUS || h->child[1]->type == _STAR ||h->child[1]->type == _DIV)) {char temp1[MAX_LENGTH], temp2[MAX_LENGTH];translateExp(h->child[0], temp1, 0);if ((temp1[0] == '#' && temp1[1] == '0') && (h->child[1]->type == _STAR || h->child[1]->type == _DIV)) {if (option == 0) {place[0] = '#';place[1] = '0';place[2] = '\0';} else {addIrCode(3, place, ":=", "#0");}}translateExp(h->child[2], temp2, 0);if ((temp2[0] == '#' && temp2[1] == '0') && (h->child[1]->type == _STAR || h->child[1]->type == _DIV)) {if (option == 0) {place[0] = '#';place[1] = '0';place[2] = '\0';} else {addIrCode(3, place, ":=", "#0");}} else if (temp1[0] == '#' && temp2[0] == '#') {int i1 = strtol(&temp1[1], NULL, 10);int i2 = strtol(&temp2[1], NULL, 10);if (h->child[1]->type == _PLUS) {if (option == 0) {sprintf(place, "#%d", i1 + i2);} else {char temp[MAX_LENGTH];sprintf(temp, "#%d", i1 + i2);addIrCode(3, place, ":=", temp);}} else if (h->child[1]->type == _MINUS) {if (option == 0) {sprintf(place, "#%d", i1 - i2);} else {char temp[MAX_LENGTH];sprintf(temp, "#%d", i1 - i2);addIrCode(3, place, ":=", temp);}} else if (h->child[1]->type == _STAR) {if (option == 0) {sprintf(place, "#%d", i1 * i2);} else {char temp[MAX_LENGTH];sprintf(temp, "#%d", i1 * i2);addIrCode(3, place, ":=", temp);}} else {if (option == 0) {sprintf(place, "#%d", i1 / i2);} else {char temp[MAX_LENGTH];sprintf(temp, "#%d", i1 / i2);addIrCode(3, place, ":=", temp);}}} else if (temp2[0] == '#' && temp2[1] == '0') {if (option == 0)strcpy(place, temp1);elseaddIrCode(3, place, ":=", temp1);} else if (temp1[0] == '#' && temp1[1] == '0' && h->child[1]->type == _PLUS) {if (option == 0)strcpy(place, temp2);elseaddIrCode(3, place, ":=", temp2);} else {char op[2];op[1] = '\0';if (h->child[1]->type == _PLUS) op[0] = '+';else if (h->child[1]->type == _MINUS) op[0] = '-';else if (h->child[1]->type == _STAR) op[0] = '*';else op[0] = '/';if (option == 0) {newTemp(place);}addIrCode(5, place, ":=", temp1, op, temp2);}} else if (h->child[0]->type == _MINUS) {char temp[MAX_LENGTH];translateExp(h->child[1], temp, 0);if (temp[0] == '#') {int i = strtol(&temp[1], NULL, 10);if (option == 0) {sprintf(place, "#%d", -i);} else {char temp1[MAX_LENGTH];sprintf(temp1, "#%d", -i);addIrCode(3, place, ":=", temp1);}} else {if (option == 0) {newTemp(place);}addIrCode(5, place, ":=", "#0", "-", temp);}} else if (h->child[0]->type == _NOT || h->child[1]->type == _RELOP || h->child[1]->type == _AND ||h->child[1]->type == _OR) {char label1[MAX_LENGTH], label2[MAX_LENGTH];newLabel(label1);newLabel(label2);if (option == 0)newTemp(place);addIrCode(3, place, ":=", "#0");translateCond(h, label1, label2);addIrCode(3, "LABEL", label1, ":");addIrCode(3, place, ":=", "#1");addIrCode(3, "LABEL", label2, ":");} else if (h->childNum == 4 && h->child[1]->type == _LB) {char temp1[MAX_LENGTH], temp2[MAX_LENGTH];getLocation(h->child[0], temp1);translateExp(h->child[2], temp2, 0);if (temp2[0] == '#' && temp2[1] == '0') {char temp[MAX_LENGTH];if (temp1[0] == '&')strcpy(temp, &temp1[1]);elsesprintf(temp, "*%s", temp1);if (option == 0) {strcpy(place, temp);} else {addIrCode(3, place, ":=", temp);}} else if (temp2[0] == '#') {int i = strtol(&temp2[1], NULL, 10);char temp[MAX_LENGTH], tempi[MAX_LENGTH];sprintf(tempi, "#%d", i * currentSize);newTemp(temp);addIrCode(5, temp, ":=", temp1, "+", tempi);char result[MAX_LENGTH];sprintf(result, "*%s", temp);if (option == 0)strcpy(place, result);elseaddIrCode(3, place, ":=", result);} else {char temp[MAX_LENGTH], tempi[MAX_LENGTH], result[MAX_LENGTH], num[MAX_LENGTH];sprintf(num, "#%d", currentSize);newTemp(temp);newTemp(tempi);addIrCode(5, tempi, ":=", temp2, "*", num);addIrCode(5, temp, ":=", temp1, "+", tempi);sprintf(result, "*%s", temp);if (option == 0)strcpy(place, result);elseaddIrCode(3, place, ":=", result);}} else if (h->childNum == 3 && h->child[1]->type == _DOT) {char left[MAX_LENGTH], temp[MAX_LENGTH], result[MAX_LENGTH];getLocation(h->child[0], left);int offseti = structGetOffset(currentStruct, h->child[2]->name);char offset[MAX_LENGTH];sprintf(offset, "#%d", offseti);newTemp(temp);addIrCode(5, temp, ":=", left, "+", offset);sprintf(result, "*%s", temp);if (option == 0)strcpy(place, result);elseaddIrCode(3, place, ":=", result);}
}

3、translateStmt
和translate函数类似,匹配每一个产生式做出相应的操作,其中可以用到translateExp处理exp结点:

void translateStmt(Node *h) {switch (h->child[0]->type) {case Exp: {translateExp(h->child[0], NULL, 0);break;}case CompSt: {translate(h->child[0]);break;}case _RETURN: {char temp[MAX_LENGTH];translateExp(h->child[1], temp, 0);addIrCode(2, "RETURN", temp);break;}case _IF: {char label1[MAX_LENGTH], label2[MAX_LENGTH], label3[MAX_LENGTH];newLabel(label1);newLabel(label2);if (h->childNum == 7) {newLabel(label3);}translateCond(h->child[2], label1, label2);addIrCode(3, "LABEL", label1, ":");translate(h->child[4]);if (h->childNum == 7)addIrCode(2, "GOTO", label3);addIrCode(3, "LABEL", label2, ":");if (h->childNum == 7) {translate(h->child[6]);addIrCode(3, "LABEL", label3, ":");}break;}case _WHILE: {char label1[MAX_LENGTH], label2[MAX_LENGTH], label3[MAX_LENGTH];newLabel(label1);newLabel(label2);newLabel(label3);addIrCode(3, "LABEL", label1, ":");translateCond(h->child[2], label2, label3);addIrCode(3, "LABEL", label2, ":");translate(h->child[4]);addIrCode(2, "GOTO", label1);addIrCode(3, "LABEL", label3, ":");break;}}
}

4、translateArgs
(1)如果Args→Exp COMMA Args,则继续处理Args,即函数的第一个if语句。
(2)分情况考虑,如果exp不为数组或结构,则翻译exp,即函数的第二个if语句。
(3)否则,exp为数组或结构。首先使用getlocation函数获取其地址,然后将其加入irCode。

void translateArgs(Node *h, VarDefUnit **args, int count) {if (h->childNum == 3)translateArgs(h->child[2], args, count + 1);if (args[count]->varType != _ARR_STRUCT_) {char temp[MAX_LENGTH];translateExp(h->child[0], temp, 0);addIrCode(2, "ARG", temp);}else {char temp[MAX_LENGTH];getLocation(h->child[0], temp);addIrCode(2, "ARG", temp);}
}

三、实验结果

  1. t1.cmm
int main()
{int n;n = read();if (n > 0) write(1); else if (n < 0) write (-1);else write(0);return 0;
}

执行中间代码生成:

中间代码:

FUNCTION main :
READ n
IF n > #0 GOTO label0
GOTO label1
LABEL label0 :
WRITE #1
GOTO label2
LABEL label1 :
IF n < #0 GOTO label3
GOTO label4
LABEL label3 :
WRITE #-1
GOTO label5
LABEL label4 :
WRITE #0
LABEL label5 :
LABEL label2 :
RETURN #0

中间代码执行结果(输入 2):

  1. t2.cmm
int fact(int n)
{if (n == 1)return n;elsereturn (n * fact(n - 1));
}int main()
{int m, result;m = read();if (m > 1)result = fact(m);elseresult = 1;write(result);return 0;
}

执行中间代码生成:

中间代码:

FUNCTION fact :
PARAM n
IF n == #1 GOTO label0
GOTO label1
LABEL label0 :
RETURN n
GOTO label2
LABEL label1 :
_t0 := n - #1
ARG _t0
_t1 := CALL fact
_t2 := n * _t1
RETURN _t2
LABEL label2 :
FUNCTION main :
READ m
IF m > #1 GOTO label3
GOTO label4
LABEL label3 :
ARG m
result := CALL fact
GOTO label5
LABEL label4 :
result := #1
LABEL label5 :
WRITE result
RETURN #0

中间代码执行结果(输入 5):

  1. t3.cmm
struct Operands
{int o1;int o2;
};int add(struct Operands temp)
{return (temp.o1 + temp.o2);
}int main()
{int n;struct Operands op;op.o1 = 1;op.o2 = 2;n = add(op);write(n);return 0;
}

执行中间代码生成:

中间代码:

FUNCTION add :
PARAM temp
_t0 := temp + #0
_t1 := temp + #4
_t2 := *_t0 + *_t1
RETURN _t2
FUNCTION main :
DEC op 8
_t3 := &op + #0
*_t3 := #1
_t4 := &op + #4
*_t4 := #2
ARG &op
n := CALL add
WRITE n
RETURN #0

中间代码执行结果:

  1. t4.cmm
int add(int temp[2])
{return (temp[0] + temp[1]);
}int main()
{int op[2];int r[1][2];int i = 0, j = 0;while (i < 2) {while (j < 2) {op[j] = i + j;j = j + 1;}r[0][i] = add(op);write(r[0][i]);i = i + 1;j = 0;}return 0;
}

执行中间代码生成:

中间代码:

FUNCTION add :
PARAM temp
_t0 := temp + #4
_t1 := *temp + *_t0
RETURN _t1
FUNCTION main :
DEC op 8
DEC r 8
i := #0
j := #0
LABEL label0 :
IF i < #2 GOTO label1
GOTO label2
LABEL label1 :
LABEL label3 :
IF j < #2 GOTO label4
GOTO label5
LABEL label4 :
_t3 := j * #4
_t2 := &op + _t3
_t8 := i + j
*_t2 := _t8
j := j + #1
GOTO label3
LABEL label5 :
_t5 := i * #8
_t4 := &r + _t5
ARG &op
_t9 := CALL add
*_t4 := _t9
_t7 := i * #8
_t6 := &r + _t7
WRITE *_t6
i := i + #1
j := #0
GOTO label0
LABEL label2 :
RETURN #0

中间代码执行结果:

四、附完整代码

github 链接:https://github.com/zheliku/byyl---Intermediate-Code-Generation。

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

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

相关文章

企业级Web项目中应该如何做单元测试、集成测试和功能测试?

先自我介绍下&#xff1a; 本人有过10年测试经验&#xff0c;也参与过公安部网络安全产品测试交付、华为4G 网络设备测试交付、腾讯QQ空间APP产品测试交付。 关于“企业级Web项目中应该如何做单元测试、集成测试和功能测试”这个问题&#xff0c;我想给大家唠唠&#xff0c;我…

38 - 换座位(高频 SQL 50 题基础版)

38 - 换座位 -- 方法一 select(casewhen id%21 and id(select max(id) from seat) then idwhen id%20 then id-1else id1end) as id, student fromseat order byid;-- 方法二selectif(id%20,id-1,if(id(select max(id) from Seat),id,id1)) as id,student fromSeat order by id…

陀螺仪LSM6DSV16X与AI集成(7)----FIFO数据读取与配置

陀螺仪LSM6DSV16X与AI集成.6--检测自由落体 概述视频教学样品申请源码下载主要内容生成STM32CUBEMX串口配置IIC配置CS和SA0设置串口重定向参考程序初始换管脚获取ID复位操作BDU设置设置量程设置FIFO水印设置速率使用流模式设置FIFO时间戳批处理速率使能时间戳FIFO状态寄存器演示…

Django数据驾驶舱

Django数据驾驶舱 1.项目介绍2.项目结构3.库表结构3.1 appcsdn的models3.2 appssq的models3.3 appweather的models3.4 appweibo的models 4.功能展示5.解决问题5.1 路由配置5.2 后端数据与前端echarts展示5.3 长图表丝滑滚动条 6.遗留问题7.资源分享 1.项目介绍 这里介绍本人最…

阿里云发送验证码流程

目录 1. 阿里云短信服务简介 2. 阿里云验证码发送流程 2.1 申请阿里云短信服务 2.2 短信模板及阿里云秘钥 1.开发者可以在自己的应用程序中集成短信发送功能。绑定发起测试的手机号&#xff0c;需要绑定的手机号才能成功发送验证码&#xff0c;其他的用户手机号发送的验…

如何在 Ubuntu 12.04 VPS 上安装和配置基本的 LDAP 服务器

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 简介 LDAP&#xff08;轻量级目录访问协议&#xff09;是一种通过文件和目录层次结构管理相关信息的协议&#xff0c;它可以从集中位置管…

【4003】基于springboot实现的线上阅读系统

作者主页&#xff1a;Java码库 主营内容&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app等设计与开发。 收藏点赞不迷路 关注作者有好处 文末获取源码 技术选型 【后端】&#xff1a;Java 【框架】&#xff1a;spring…

ARM裸机:基础了解

ARM的几种版本号 ARM内核版本号 ARMv7 ARM SoC版本号 Cortex-A8 芯片型号 S5PV210 ARM型号的发展历程 m microcontroller微控制器 就是单片机 a application应用级处理器 就是手机、平板、电脑的CPU r realtime实时处理器 响应速度快,主要用在工业、航天等领域 soc 、cpu、…

ubutu 18.04源码编译安装freeswitch 1.10.7支持视频通话——筑梦之路

软件版本说明 ubuntu版本18.04&#xff1a;https://releases.ubuntu.com/18.04.6/ubuntu-18.04.6-live-server-amd64.iso freeswitch 版本1.10.7&#xff1a;https://files.freeswitch.org/freeswitch-releases/freeswitch-1.10.7.-release.tar.gz spandsp包&#xff1a;https:…

VB计算圆柱体积和表面积

已知圆半径和圆柱的高&#xff0c;计算圆柱体积和表面积。 Public Class Form1Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.ClickConst PI 3.14159Dim r As Integer, h As IntegerDim t As Single, s As Singler Val(TextBox1.Text)h V…

免杀笔记 ---> C语言

这次的更新可能有点慢&#xff0c;因为这段时间也比较忙&#xff0c;加上C语言还得和汇编结合&#xff0c;导致小编一个知识点总是得反复揣摩&#xff08;太菜了&#xff09;&#xff0c;所以免杀的更新篇幅长度可能会达到两个月和三个月&#xff0c;但是小编能保证&#xff0c…

动手学深度学习(Pytorch版)代码实践 -卷积神经网络-24深度卷积神经网络AlexNet

24深度卷积神经网络AlexNet import torch from torch import nn import liliPytorch as lp import liliPytorch as lp import matplotlib.pyplot as pltdropout1 0.5 #Alexnet架构 net nn.Sequential(nn.Conv2d(1, 96, kernel_size11, stride4, padding1),nn.ReLU(),nn.MaxPo…

智慧校园综合管理系统的优点有哪些

在当今这个信息化飞速发展的时代&#xff0c;智慧校园综合管理系统正逐步成为教育领域的一股革新力量&#xff0c;它悄然改变着我们对传统校园管理的认知。这套系统如同一个无形的桥梁&#xff0c;将先进的信息技术与学校的日常运作紧密相连&#xff0c;展现出多维度的优势。 …

训练营第四十五天 | 435. 无重叠区间763.划分字母区间56. 合并区间738.单调递增的数字968.监控二叉树

435. 无重叠区间 力扣题目链接(opens new window) 给定一个区间的集合&#xff0c;找到需要移除区间的最小数量&#xff0c;使剩余区间互不重叠。 注意: 可以认为区间的终点总是大于它的起点。 区间 [1,2] 和 [2,3] 的边界相互“接触”&#xff0c;但没有相互重叠。 示例 1…

6/22 第四周 python操作word

学习到了word有四个段落&#xff0c;都可以通过python来操作。 并且课程的体系&#xff0c;只是一个启蒙&#xff0c;需要在公司的项目中熟悉&#xff0c;从而具备专项测试的能力。 后续每天的学习笔记也需要侧重于理解的部分。

【CPP】归并排序

目录 1.归并排序简介代码分析归并的非递归形式 1.归并排序 归并排序&#xff08;MERGE-SORT&#xff09; 是建立在归并操作上的一种有效的排序算法,该算法是采用分治法&#xff08;Divide andConquer&#xff09;的一个非常典型的应用。 将已有序的子序列合并&#xff0c;得到…

NXP实战笔记(十四):32K3xx基于RTD-SDK在S32DS实现HSE的安装。

目录 1、概述 1.1、什么是HSE&#xff1f; 1.2、如何实现HSE的OTA功能 1.3、S32K3放置HSE的地址 2、通过调试器安装HSE 3、通过IVT方式安装HSE 4、坑点慎重踩 4.1、优化等级 4.2、Flash放RAM 4.3、C40_Ip配置更改 4.4、程序烧录 5、测试结果 6、代码链接 1、概述 首…

“论SOA在企业集成架构设计中的应用”必过模板,软考高级,系统架构设计师论文

论文真题 企业应用集成(Enterprise Application Integration, EAI)是每个企业都必须要面对的实际问题。面向服务的企业应用集成是一种基于面向服务体系结构(Service-OrientedArchitecture,SOA)的新型企业应用集成技术,强调将企业和组织内部的资源和业务功能暴露为服务,实现…

vue-json-viewer组件 copyable失效,页面并不现实copy按钮

<json-viewer :value"props.row.param_detail.query" :expand-depth"10" copyable> </json-viewer> 官方文档中&#xff0c;说明&#xff0c;只要在json-viewer中加入 copyable属性&#xff0c;即可实现copy功能&#xff0c;如下图&#xff1…

yolov8环境搭建+训练自己数据集

一、yolov8环境搭建 1. 安装miniconda环境 地址&#xff1a;https://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda 选择Python3.8版本 最好安装在C盘 勾选自动添加环境变量 ***以下操作安装过程中关闭代理软件 *** 2. 创建虚拟环境 conda create -n yolov8 python3…