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

Java VMTranslator Part I

目录

堆栈运算命令

基本思路

核心代码

Parser

Code Writer

Main

实验结果,使用SimpleAdd、StackTest进行验证

内存访问命令

基本思路

核心代码

Parser

Code Writer

Main

实验结果,使用进行验证。对比生成的二进制代码文件。


用Java写一个翻译器,将Java的字节码翻译成汇编语言 

堆栈运算命令

基本思路

主要写两个类,一个解析器类Parser负责处理输入的vm文件,解析vm指令,一个类CodeWriter负责将经过Parser解析过的vm指令翻译成汇编指令,输出asm文件。

首先编写类Parser,有六个成员函数,包含构造函数负责打开文件流并准备读取vm指令,hasMoreCommands函数判断是否还有指令,advance函数负责将vm指令处理干净,去掉空白和注释,commandType函数判断vm指令的类型,arg1和arg2函数负责返回指令的组成部分。

然后编写CodeWriter类,构造函数打开一个文件,准备写入asm指令,writeArithmetic函数写入算术逻辑运算asm指令,writerPushPop函数写入push和pop的asm指令。

然后最关键的地方来了,如何从vm指令到asm指令?

我们首先从算术逻辑运算指令来看,以二元运算为例,计算的两个数是放在栈上的,位于栈指针SP上面两个位置,而我们只有M和D两个寄存器可以用来计算,在A寄存器保存栈指针地址的情况下。

因此,对于所有二元运算,我们首先要把参与计算的两个数放在M和D寄存器上,具体操作是,栈指针SP自减,把M的值赋给D,然后再将栈指针上移。

然后再执行二元运算,这样就比较简单了。

对于一元运算比较简单,直接栈指针自减,对M进行操作即可。

而对于gt、eq和lt这样比较指令,则比较复杂,因为涉及到跳转,同样是二元运算,因此我们需要先按照上述方法先将参与计算的两个数拿出来放在M和D寄存器,然后计算M和D的差,通过比较差和0的大小来跳转。

而对于push constant x指令,将一个常数压入栈就比较简单了。

拿到常数的值后将它写入栈指针执行的内存,然后栈指针自增就行了。

核心代码

Parser

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.Objects;
import java.util.Scanner;public class Parser {private String command = null;private Scanner scanner = null;private String cmd0 = null;private String cmd1 = null;private int cmd2;public Parser(File file) throws FileNotFoundException {scanner = new Scanner(new FileReader(file));}public boolean hasMoreCommands() {boolean hasMore = false;while (scanner.hasNextLine()) {command = scanner.nextLine();if (!Objects.equals(command, "") && command.charAt(0) != '/') { //去掉空白行和注释String[] pure = command.split("/");command = pure[0];hasMore = true;break;}}return hasMore;}public void advance() {String[] cmd = command.split(" ");cmd0 = cmd[0];if (cmd.length > 1) {cmd1 = cmd[1];if (cmd.length > 2) {cmd2 = Integer.parseInt(cmd[2]);}}}public String commandType() {if (Objects.equals(cmd0, "push")) {return "C_PUSH";} else {return "C_ARITHMETIC";}}public String arg1() {if (Objects.equals(commandType(), "C_ARITHMETIC"))return cmd0;return cmd1;}public int arg2() {return cmd2;}public void close(){scanner.close();}}

Code Writer

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Objects;public class CodeWriter {private FileWriter asm = null;private String asmCommand;private final HashMap<String, String> vmToAsm = new HashMap<>();private int jump=0;public CodeWriter(File file) throws IOException {asm = new FileWriter(file);String fetch = "@SP\nM=M-1\nA=M\nD=M\nA=A-1\n";vmToAsm.put("add", fetch + "M=M+D\n");vmToAsm.put("sub", fetch + "M=M-D\n");vmToAsm.put("and", fetch + "M=M&D\n");vmToAsm.put("or", fetch + "M=M|D\n");vmToAsm.put("gt", fetch + "D=M-D\n@TRUE\nD;JGT\n@SP\nA=M-1\nM=0\n@END\n0;JMP\n(TRUE)\n@SP\nA=M-1\nM=-1\n(END)\n");vmToAsm.put("eq", fetch + "D=M-D\n@TRUE\nD;JEQ\n@SP\nA=M-1\nM=0\n@END\n0;JMP\n(TRUE)\n@SP\nA=M-1\nM=-1\n(END)\n");vmToAsm.put("lt", fetch + "D=M-D\n@TRUE\nD;JLT\n@SP\nA=M-1\nM=0\n@END\n0;JMP\n(TRUE)\n@SP\nA=M-1\nM=-1\n(END)\n");vmToAsm.put("neg", "D=0\n@SP\nA=M-1\nM=D-M\n");vmToAsm.put("not", "@SP\nA=M-1\nM=!M\n");}public void writeArithmetic(String vmCommand) throws IOException {asmCommand=vmToAsm.get(vmCommand);if(Objects.equals(vmCommand, "gt") || Objects.equals(vmCommand, "eq") || Objects.equals(vmCommand, "lt")){asmCommand=asmCommand.replaceAll("TRUE","TRUE"+Integer.toString(jump));asmCommand=asmCommand.replaceAll("END","END"+Integer.toString(jump));jump++;}asm.write(asmCommand);}public void writePushPop(String cmd, String segment, int index) throws IOException {if (Objects.equals(cmd, "C_PUSH")) {if (Objects.equals(segment, "constant")) {asmCommand = "@" + Integer.toString(index) + "\nD=A\n@SP\nA=M\nM=D\n@SP\nM=M+1\n";}}asm.write(asmCommand);}public void close() throws IOException {asm.close();}
}

Main

import java.io.File;
import java.io.IOException;
import java.util.Objects;public class Main {public static void main(String[] args) throws IOException {Parser parser=new Parser(new File("C:\\Users\\Yezi\\Desktop\\Java程序设计\\nand2tetris\\projects\\07\\StackArithmetic\\StackTest\\StackTest.vm"));CodeWriter codeWriter=new CodeWriter(new File("C:\\Users\\Yezi\\Desktop\\Java程序设计\\nand2tetris\\projects\\07\\StackArithmetic\\StackTest\\StackTest.asm"));while(parser.hasMoreCommands()){parser.advance();if(Objects.equals(parser.commandType(), "C_ARITHMETIC")){codeWriter.writeArithmetic(parser.arg1());}else{codeWriter.writePushPop(parser.commandType(), parser.arg1(), parser.arg2());}}codeWriter.close();parser.close();}
}

实验结果,使用SimpleAdd、StackTest进行验证

我们用CPU Emulator装载.tst文件,用运行程序得到的.out文件和所给的.cmp文件进行比较,其中SimpleAdd比较结果如下图所示,可见成功翻译

StackTest的结果如下图所示,可见第一阶段翻译成功。

内存访问命令

基本思路

首先要搞明白的是,push操作是将内存上的数值压入栈中,而pop操作是将栈中的数值弹出来到内存中。

对于constant段,我们第一阶段已经解决了。

对于local、argument、this和that字段,就是从它们相应的内存地址上读取或写入数据。

Push的话,先拿到segment+i的地址所指向的数值,然后将这个数值压入栈中,栈指针自增。

Pop的话,要复杂一些,因为我们只有A、M和D寄存器可以用,而pop我们首先要拿到segment+i的地址,所以我们要先找一个地方存下来,原本的R系列寄存器在这里已经被字段占用了,所以我们这里取地址255的内存空间暂存一下地址。

而temp字段的push和pop操作相对而言要简单许多。

此时读写的地址为5+i。

对于pointer字段,其实就是把this和that的数值压入栈或者弹栈的数值到this和that中。

当参数为0时,对this进行操作,当参数为1时,对that进行操作,在this和that的地址上进行读写数据。

而对于static字段,与前面的字段相比,不过就是换了运算的地址空间而已。

Asm代码基本和前面的操作一样,就是运算的地址变成了16开始的地址。

核心代码

Parser

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.Objects;
import java.util.Scanner;public class Parser {private String command = null;private final Scanner scanner;private String cmd0 = null;private String cmd1 = null;private int cmd2;public Parser(File file) throws FileNotFoundException {scanner = new Scanner(new FileReader(file));}public boolean hasMoreCommands() {boolean hasMore = false;while (scanner.hasNextLine()) {command = scanner.nextLine();if (!Objects.equals(command, "") && command.charAt(0) != '/') { //去掉空白行和注释String[] pure = command.split("/");command = pure[0];hasMore = true;break;}}return hasMore;}public void advance() {String[] cmd = command.split(" ");cmd0 = cmd[0];if (cmd.length > 1) {cmd1 = cmd[1];if (cmd.length > 2) {cmd2 = Integer.parseInt(cmd[2]);}}}public String commandType() {if (Objects.equals(cmd0, "push")) {return "C_PUSH";} else if (Objects.equals(cmd0, "pop")) {return "C_POP";} else {return "C_ARITHMETIC";}}public String arg1() {if (Objects.equals(commandType(), "C_ARITHMETIC"))return cmd0;return cmd1;}public int arg2() {return cmd2;}public void close() {scanner.close();}}

Code Writer

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Objects;public class CodeWriter {private final FileWriter asm;private String asmCommand;private final HashMap<String, String> vmToAsm = new HashMap<>();private int jump = 0;public CodeWriter(File file) throws IOException {asm = new FileWriter(file);String fetch = "@SP\nM=M-1\nA=M\nD=M\nA=A-1\n";vmToAsm.put("add", fetch + "M=M+D\n");vmToAsm.put("sub", fetch + "M=M-D\n");vmToAsm.put("and", fetch + "M=M&D\n");vmToAsm.put("or", fetch + "M=M|D\n");vmToAsm.put("gt", fetch + "D=M-D\n@TRUE\nD;JGT\n@SP\nA=M-1\nM=0\n@END\n0;JMP\n(TRUE)\n@SP\nA=M-1\nM=-1\n(END)\n");vmToAsm.put("eq", fetch + "D=M-D\n@TRUE\nD;JEQ\n@SP\nA=M-1\nM=0\n@END\n0;JMP\n(TRUE)\n@SP\nA=M-1\nM=-1\n(END)\n");vmToAsm.put("lt", fetch + "D=M-D\n@TRUE\nD;JLT\n@SP\nA=M-1\nM=0\n@END\n0;JMP\n(TRUE)\n@SP\nA=M-1\nM=-1\n(END)\n");vmToAsm.put("neg", "D=0\n@SP\nA=M-1\nM=D-M\n");vmToAsm.put("not", "@SP\nA=M-1\nM=!M\n");}public void writeArithmetic(String vmCommand) throws IOException {asmCommand = vmToAsm.get(vmCommand);if (Objects.equals(vmCommand, "gt") || Objects.equals(vmCommand, "eq") || Objects.equals(vmCommand, "lt")) {asmCommand = asmCommand.replaceAll("TRUE", "TRUE" + jump);asmCommand = asmCommand.replaceAll("END", "END" + jump);jump++;}asm.write(asmCommand);}public void writePushPop(String cmd, String segment, int index) throws IOException {if (Objects.equals(cmd, "C_PUSH")) {if (Objects.equals(segment, "constant")) {asmCommand = "@" + index + "\nD=A\n@SP\nA=M\nM=D\n@SP\nM=M+1\n";} else if (Objects.equals(segment, "local")) {asmCommand = "@LCL\nD=M\n@" + index + "\nA=D+A\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n";} else if (Objects.equals(segment, "argument")) {asmCommand = "@ARG\nD=M\n@" + index + "\nA=D+A\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n";} else if (Objects.equals(segment, "this")) {asmCommand = "@THIS\nD=M\n@" + index + "\nA=D+A\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n";} else if (Objects.equals(segment, "that")) {asmCommand = "@THAT\nD=M\n@" + index + "\nA=D+A\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n";} else if (Objects.equals(segment, "temp")) {asmCommand = "@" + (5 + index) + "\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n";} else if (Objects.equals(segment, "pointer")) {if (index == 0) {asmCommand = "@THIS\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n";} else {asmCommand = "@THAT\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n";}} else if (Objects.equals(segment, "static")) {asmCommand = "@" + (16 + index) + "\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n";}} else {if (Objects.equals(segment, "local")) {asmCommand = "@LCL\nD=M\n@" + index + "\nD=D+A\n@255\nM=D\n@SP\nM=M-1\nA=M\nD=M\n@255\nA=M\nM=D\n";} else if (Objects.equals(segment, "argument")) {asmCommand = "@ARG\nD=M\n@" + index + "\nD=D+A\n@255\nM=D\n@SP\nM=M-1\nA=M\nD=M\n@255\nA=M\nM=D\n";} else if (Objects.equals(segment, "this")) {asmCommand = "@THIS\nD=M\n@" + index + "\nD=D+A\n@255\nM=D\n@SP\nM=M-1\nA=M\nD=M\n@255\nA=M\nM=D\n";} else if (Objects.equals(segment, "that")) {asmCommand = "@THAT\nD=M\n@" + index + "\nD=D+A\n@255\nM=D\n@SP\nM=M-1\nA=M\nD=M\n@255\nA=M\nM=D\n";} else if (Objects.equals(segment, "temp")) {asmCommand = "@SP\nM=M-1\nA=M\nD=M\n@" + (5 + index) + "\nM=D\n";} else if (Objects.equals(segment, "pointer")) {if (index == 0) {asmCommand = "@SP\nM=M-1\nA=M\nD=M\n@THIS\nM=D\n";} else {asmCommand = "@SP\nM=M-1\nA=M\nD=M\n@THAT\nM=D\n";}} else if (Objects.equals(segment, "static")) {asmCommand = "@SP\nM=M-1\nA=M\nD=M\n@" + (16 + index) + "\nM=D\n";}}asm.write(asmCommand);}public void close() throws IOException {asm.close();}
}

Main

main函数没变

实验结果,使用进行验证。对比生成的二进制代码文件。

我们用CPU Emulator装载.tst文件,用运行程序得到的.out文件和所给的.cmp文件进行比较,其中BasicTest的比较结果如下图所示,可见成功翻译。

PointerTest的比较结果如下图所示,可见成功翻译

StaticTest的结果如下图所示,可见第二阶段翻译成功。

相关文章:

Java VMTranslator Part I

目录 堆栈运算命令 基本思路 核心代码 Parser Code Writer Main 实验结果&#xff0c;使用SimpleAdd、StackTest进行验证 内存访问命令 基本思路 核心代码 Parser Code Writer Main 实验结果&#xff0c;使用进行验证。对比生成的二进制代码文件。 用Java写一个翻…...

ES6带来那些js新特性?

ECMAScript 6&#xff08;ES6&#xff09;&#xff0c;也称为 ECMAScript 2015&#xff0c;引入了许多重大的改进和新特性&#xff0c;以改善JavaScript语言的功能和可读性。以下是一些ES6中的主要改变和新特性&#xff1a; 1、let 和 const 声明: 引入了 let 和 const 关键字…...

js数组深拷贝汇总

1.for 循环实现数组的深拷贝 通过对数组的for循环&#xff0c;即可实现对数组的深拷贝了。 var arr [1,2,3,4,5] var arr2 copyArr(arr) function copyArr(arr) {let res []for (let i 0; i < arr.length; i) {res.push(arr[i])}return res }2.slice 方法实现数组的深…...

错误 LNK1112 模块计算机类型“x64”与目标计算机类型“X86”冲突

这个错误表明你在进行链接时&#xff0c;模块的计算机类型与目标计算机类型冲突。 在这里&#xff0c;“x64”代表64位系统&#xff0c;“X86”代表32位系统。 要解决这个问题&#xff0c;你需要确保你的所有模块&#xff08;包括库文件和依赖项&#xff09;都是与你的目标计…...

java八股文(基础篇)

面向过程和面向对象的区别 面向过程&#xff1a;在解决问题时&#xff0c;特别自定义函数编写一步一步的步骤解决问题。 面向对象&#xff1a;其特点就是 继承&#xff0c;多态&#xff0c;继承&#xff0c;在解决问题时&#xff0c;不再注重函数的编写&#xff0c;而在于注重…...

window系统修改rabbitmq 默认端口

安装完rabbitmq之后&#xff0c;默认的client端口是5672, 控制台访问端口是15672&#xff0c;rabbitmq管理工具启动之后在浏览器中输入地址&#xff1a; ​ ​http://localhost:15672/​​​ 就可以访问后台​ ​​​&#xff0c; 默认管理员账号&#xff1a;guest 密码&#x…...

七人拼团模式:颠覆你的购物观念,499元产品让你赚翻天!

七人拼团模式是一种创新的消费模式&#xff0c;通过聚集消费者的购买力&#xff0c;让消费者能够以更优惠的价格购买到优质的商品。下面我们以499元的产品为例&#xff0c;详细介绍七人拼团模式的玩法规则和收益计算。 玩法规则&#xff1a; 消费者购买499元的指定产品后&…...

【机器学习合集】模型设计之卷积核设计 ->(个人学习记录笔记)

文章目录 卷积核设计1. 基于参数压缩的卷积设计1.1 【11卷积】1.2 【11卷积典型应用】1.3 【小卷积的使用】 2. 基于感受野的卷积设计2.1 膨胀卷积(带孔卷积&#xff0c;strous convolution)2.2 可变形卷积2.3 非局部卷积 3. 基于卷积操作的优化3.1 移位网络3.2 加法网络 卷积核…...

JS实现用户二次确认后再提交表单

HTML代码 <form id"importForm" action"" method"post" enctype"multipart/form-data" onsubmit"return confirmSubmit()"> ...... <input id"btnImportSubmit" class"btn btn-primary" type…...

1992-2021年全国各省经过矫正的夜间灯光数据(GNLD、VIIRS)

1992-2021年省市县经过矫正的夜间灯光数据&#xff08;GNLD、VIIRS&#xff09; 1、时间&#xff1a;1992-2021年3月&#xff0c;其中1992-2013年为年度数据&#xff0c;2013-2021年3月为月度数据 2、来源&#xff1a;DMSP、VIIRS 3、范围&#xff1a;31省 4、指标解释&…...

JMeter的使用——傻瓜式学习【中】

目录 前言 1、JMeter参数化 1.1、什么是参数化 1.2、用户定义的变量 1.2.1、什么时候使用用户定义的变量 1.2.2、使用“用户定义的变量”进行参数化的步骤&#xff1a; 1.2.3、案例 1.3、用户参数 1.3.1、什么时候使用用户参数&#xff1f; 1.3.2、使用“用户参数”进…...

MyBaties存储和查询json格式的数据(实体存储查询版本)

最近在做的功能&#xff0c;由于别的数据库有值&#xff0c;需要这边的不同入口的进来查询&#xff0c;所以需要同步过来&#xff0c;如果再继续一个一个生成列对应处理感觉不方便&#xff0c;如果没有别的操作&#xff0c;只是存储和查询&#xff0c;那就可以用MySql支持的jso…...

动态规划14:一和零

动态规划14&#xff1a;一和零 题目 474. 一和零 给你一个二进制字符串数组 strs 和两个整数 m 和 n 。 请你找出并返回 strs 的最大子集的长度&#xff0c;该子集中 最多 有 m 个 0 和 n 个 1 。 如果 x 的所有元素也是 y 的元素&#xff0c;集合 x 是集合 y 的 子集 。 …...

C#WPF嵌入字体实例

本文介绍C#WPF嵌入字体实例。 首先创建项目 添加Resources文件夹,添加字体文件,字体文件属性:生成操作为Resources,复制到输出目录:不复制 字体的使用可以采用以下两种方法: 方式一 直接引用 FontFamily="./Resources/#幼圆" 方式二 定义资源 <Applica…...

Linux——Linux权限

Linux权限 前言一、shell命令以及运行原理二、Linux权限的概念Linux权限管理文件访问者的分类&#xff08;人&#xff09;文件类型和访问权限&#xff08;事物属性&#xff09;文件权限值的表示方法文件访问权限的相关设置方法 file指令目录的权限粘滞位 总结 前言 linux的学习…...

android中gradle的kotlin编译配置选项

一、编译配置 1、Android中的配置 使用如下方式开启在Android中的gradle的kotlin编译配置&#xff1a; 该配置在其余平台不可用 android {...compileOptions {sourceCompatibility JavaVersion.VERSION_17targetCompatibility JavaVersion.VERSION_17}kotlinOptions {jvmTar…...

【知识串联】概率论中的值和量(随机变量/数字特征/参数估计)【考研向】【按概率论学习章节总结】(最大似然估计量和最大似然估计值的区别)

就我的概率论学习经验来看&#xff0c;这两个概念极易混淆&#xff0c;并且极为重点&#xff0c;然而&#xff0c;在概率论的前几章学习中&#xff0c;如果只是计算&#xff0c;对这方面的辨析不清并没有问题。然而&#xff0c;到了后面的参数估计部分&#xff0c;却可能出现问…...

NOIP2023模拟6联测27 点餐

题目大意 有 n n n样菜品&#xff0c;每样菜品都有两个权值 a i a_i ai​和 b i b_i bi​&#xff0c;如果你选择了 k k k个菜品&#xff0c;分别为 p 1 , … , p k p_1,\dots,p_k p1​,…,pk​&#xff0c;则你的花费为 ∑ i 1 k a p i max ⁡ i 1 k b p i \sum\limits_{i…...

AMEYA360:类比半导体重磅发布车规级智能高边驱动HD7xxxQ系列

致力于提供高品质芯片的国内优秀模拟及数模混合芯片设计商上海类比半导体技术有限公司(下称“类比半导体”或“类比”)宣布推出重磅新品车规级智能高边驱动HD7xxxQ系列。该系列产品包括车规级单通道高边驱动HD70xxQ和车规级双通道智能高边驱动HD70xx2Q&#xff0c;提供不同通道…...

【HarmonyOS】鸿蒙操作系统架构

HarmonyOS架构 一. 鸿蒙系统定位二. 架构整体遵从分层设计三. HarmonyOS具有的技术特性四. HarmonyOS有三大特征 其它相关推荐&#xff1a; 软考系统架构之案例篇(架构设计相关概念) 系统架构之微服务架构 系统架构设计之微内核架构 所属专栏&#xff1a;系统架构设计师 一. 鸿…...

云计算——弹性云计算器(ECS)

弹性云服务器&#xff1a;ECS 概述 云计算重构了ICT系统&#xff0c;云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台&#xff0c;包含如下主要概念。 ECS&#xff08;Elastic Cloud Server&#xff09;&#xff1a;即弹性云服务器&#xff0c;是云计算…...

Unity3D中Gfx.WaitForPresent优化方案

前言 在Unity中&#xff0c;Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染&#xff08;即CPU被阻塞&#xff09;&#xff0c;这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案&#xff1a; 对惹&#xff0c;这里有一个游戏开发交流小组&…...

centos 7 部署awstats 网站访问检测

一、基础环境准备&#xff08;两种安装方式都要做&#xff09; bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats&#xff0…...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

C# 类和继承(抽象类)

抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...

CMake控制VS2022项目文件分组

我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...

基于TurtleBot3在Gazebo地图实现机器人远程控制

1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...

Mysql中select查询语句的执行过程

目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析&#xff08;Parser&#xff09; 2.4、执行sql 1. 预处理&#xff08;Preprocessor&#xff09; 2. 查询优化器&#xff08;Optimizer&#xff09; 3. 执行器…...

安全突围:重塑内生安全体系:齐向东在2025年BCS大会的演讲

文章目录 前言第一部分&#xff1a;体系力量是突围之钥第一重困境是体系思想落地不畅。第二重困境是大小体系融合瓶颈。第三重困境是“小体系”运营梗阻。 第二部分&#xff1a;体系矛盾是突围之障一是数据孤岛的障碍。二是投入不足的障碍。三是新旧兼容难的障碍。 第三部分&am…...

数据结构:泰勒展开式:霍纳法则(Horner‘s Rule)

目录 &#x1f50d; 若用递归计算每一项&#xff0c;会发生什么&#xff1f; Horners Rule&#xff08;霍纳法则&#xff09; 第一步&#xff1a;我们从最原始的泰勒公式出发 第二步&#xff1a;从形式上重新观察展开式 &#x1f31f; 第三步&#xff1a;引出霍纳法则&…...