Javac编译原理:基本结构和工作原理

news2024/11/17 17:45:31

Javac编译器

文章目录

  • Javac编译器
    • 简介
    • 基本结构
      • 如何编译程序
    • 工作原理
      • 词法分析器
      • 语法分析器
      • 语义分析器
      • 代码生成器

简介

javac是一种编译器,能将一种语言规范转化成另一种语言规范

编译器通常是将便于人理解的语言规范转换成容易理解的语言规范,如C都是将源码直接编译成目标机器码,这个目标机器码是CPU直接执行的指令集合,这些指令集合也就是底层的一种语言规范,机器能够直接识别这种语言规范,虽然这种机器码执行起来高效,但是对人不友好,开发这个代码的成本远远高于省下的机器的执行成本

从某种程度上来说,有了编译器才有了程序语言的繁荣,编译器是人类和机器沟通的一个纽带

javac的任务:将Java源代码语言先转换成JVM能识别的一种语言,再由JVM将JVM语言转化成当前机器能识别的机器语言

Java语言的执行和平台无关,也成就了Java语言的繁荣

image-20230615192000812

从图中可以看出,Javac的任务就是将Java源码编译成Java字节码,也就是JVM能够识别的二进制码,(.java - .class),这些二进制数字是有格式的,只有JVM能正确识别

基本结构

Javac编译器的作用:将符合Java语言规范的源代码转化成符合Java虚拟机规范的Java字节码

如何编译程序

Javac主要四个模块:词法分析器、语法分析器、语义分析器和代码生成器

词法分析:一个字节为一节的读,找出语法关键词,最终从源代码中找出一些规范化的Token流

语法分析:检查关键词组合在一起是否符合Java语言规范,形成一个符合Java语法规范的抽象语法树,抽象语法树是一个结构化的语法表达形式,作用:把语言的主要词法用一个结构化的形式组织在一起,这颗语法树在之后可以按照新的规则重新组织,也是编译器的关键所在

语义分析:把一些难懂的、复杂的语法转换成更加简单的语法,结果就是将复杂语法转换成简单语法,还有注解,形成一个注解过后的抽象语法树,这棵语法树更加接近目标语言的语法规则

代码生成:通过字节码生成器生成字节码,根据经过注解的抽象语法树生成字节码,也就是将一个数据结构转换成另一个数据结构,代码生成器的结果就是生成符合Java虚拟机规范的字节码

Javac组件

工作原理

词法分析器

作用:将Java源文件的字符流转变成对应的Token流

类结构

Javac主要词法分析器的接口类是

package com.sun.tools.javac.parser;
public interface Lexer {

它的默认实现类是

package com.sun.tools.javac.parser;
public class Scanner implements Lexer {

Scanner会逐个读取Java源文件的单个字符,然后解析出符合Java语言规范的Token序列,所涉及的类:

image-20230615203432973

由Factory生成了两个接口的实现类:Scanner和JavacParser,这两个类负责整个词法分析的过程控制;

JavacParser:规定哪些词是符合Java语言规范规定的

Scanner:读取和归类不同词法的操作

Token:规定了所有Java语言的合法关键词

Names:存储和表示解析后的词法

词法分析过程是在JavacParser的该方法中完成的:

    /** CompilationUnit = [ { "@" Annotation } PACKAGE Qualident ";"] {ImportDeclaration} {TypeDeclaration}
     */
    public JCTree.JCCompilationUnit parseCompilationUnit() {
        Token firstToken = token;
        JCModifiers mods = null;
        boolean consumedToplevelDoc = false;
        boolean seenImport = false;
        boolean seenPackage = false;
        ListBuffer<JCTree> defs = new ListBuffer<>();
        //解析修饰符
        if (token.kind == MONKEYS_AT)
            mods = modifiersOpt(); 

        //解析package声明
        if (token.kind == PACKAGE) { 
            int packagePos = token.pos;
            List<JCAnnotation> annotations = List.nil();
            seenPackage = true;
            if (mods != null) {
                checkNoMods(mods.flags & ~Flags.DEPRECATED);
                annotations = mods.annotations;
                mods = null;
            }
            nextToken();
            JCExpression pid = qualident(false);
            accept(SEMI);
            JCPackageDecl pd = toP(F.at(packagePos).PackageDecl(annotations, pid));
            attach(pd, firstToken.comment(CommentStyle.JAVADOC));
            consumedToplevelDoc = true;
            defs.append(pd);
        }

        boolean checkForImports = true;
        boolean firstTypeDecl = true;
        while (token.kind != EOF) {
            if (token.pos <= endPosTable.errorEndPos) {
                // error recovery 跳过错误字符
                skip(checkForImports, false, false, false);
                if (token.kind == EOF)
                    break;
            }
            if (checkForImports && mods == null && token.kind == IMPORT) {
                seenImport = true;
                //解析import声明
                defs.append(importDeclaration());
            } else {
                Comment docComment = token.comment(CommentStyle.JAVADOC);
                if (firstTypeDecl && !seenImport && !seenPackage) {
                    docComment = firstToken.comment(CommentStyle.JAVADOC);
                    consumedToplevelDoc = true;
                }
                //SEMI(";"),
                if (mods != null || token.kind != SEMI)
                    mods = modifiersOpt(mods);
                if (firstTypeDecl && token.kind == IDENTIFIER) {
                    ModuleKind kind = ModuleKind.STRONG;
                    if (token.name() == names.open) {
                        kind = ModuleKind.OPEN;
                        nextToken();
                    }
                    //Token.INDENTIFIER 用于表示用户定义的名称
                    if (token.kind == IDENTIFIER && token.name() == names.module) {
                        if (mods != null) {
                            checkNoMods(mods.flags & ~Flags.DEPRECATED);
                        }
                        defs.append(moduleDecl(mods, kind, docComment));
                        consumedToplevelDoc = true;
                        break;
                    } else if (kind != ModuleKind.STRONG) {
                        reportSyntaxError(token.pos, Errors.ExpectedModule);
                    }
                }
                JCTree def = typeDeclaration(mods, docComment);
                if (def instanceof JCExpressionStatement statement)
                    def = statement.expr;
                defs.append(def);
                if (def instanceof JCClassDecl)
                    checkForImports = false;
                mods = null;
                firstTypeDecl = false;
            }
        }
        JCTree.JCCompilationUnit toplevel = F.at(firstToken.pos).TopLevel(defs.toList());
        if (!consumedToplevelDoc)
            attach(toplevel, firstToken.comment(CommentStyle.JAVADOC));
        if (defs.isEmpty())
            storeEnd(toplevel, S.prevToken().endPos);
        if (keepDocComments)
            toplevel.docComments = docComments;
        if (keepLineMap)
            toplevel.lineMap = S.getLineMap();
        this.endPosTable.setParser(null); // remove reference to parser
        toplevel.endPositions = this.endPosTable;
        return toplevel;
    }

从源文件的一个字符开始,按照Java语法规范依次找出package、import、类定义、属性和方法定义,最后构建一个抽象语法树

Javac是如何分辨一个token的呢?

    /** The factory to be used for abstract syntax tree construction.
     */
    protected TreeMaker F;
	/**
     * Qualident = Ident { DOT [Annotations] Ident }
     */
    public JCExpression qualident(boolean allowAnnos) {
        JCExpression t = toP(F.at(token.pos).Ident(ident()));
        while (token.kind == DOT) { //判断这个token是否token.DOT,如果是则读取整个package定义的类名
            int pos = token.pos;
            nextToken();
            List<JCAnnotation> tyannos = null;
            if (allowAnnos) {
                //注解
                tyannos = typeAnnotationsOpt();
            }
            t = toP(F.at(pos).Select(t, ident()));
            if (tyannos != null && tyannos.nonEmpty()) {
                t = toP(F.at(tyannos.head.pos).AnnotatedType(tyannos, t));
            }
        }
        return t;
    }

先根据Token.INDENTIFIER 的token创建一个JCIdent的语法节点,然后取下一个token,如果为DOT,则进入while循环读取整个路径

如何判断哪些字符组合是一个token则是在Scanner类中定义:

//每调用依次方法就会形成一个token    
public void nextToken() {
        prevToken = token;
        if (!savedTokens.isEmpty()) {
            token = savedTokens.remove(0);
        } else {
            token = tokenizer.readToken();
        }
    }

实际上在读取每个token时都需要一个转换过程,在java源码中的所有字符集合都要找到在com.sun.tools.javac.parser.INDENTIFIER中定义的对应关系,这个任务则是在com.sun.tools.javac.parser.Keywords中完成,Kaywords负责将所有字符集合对应到token集合中

字符集合到token转换相关的类关系:

image-20230615212043243

每个字符集合都是一个Name对象,所有的Name对象都存储在Name.Table这个内部类,这个类也就是对应的这个类的符号表,会将所有的元素按照他们的Token.name转化成name对象,然后建立Name对象和Token的对应关系,保存在Keyworks里的key数组,其他的所有字符集合则会被对应到Token.INDENTIFIER类型

image-20230616131231850

语法分析器

作用:将Token流组建成更加结构化的语法树

语法树的规则

每个语法节点都会实现一个接口xxxTree,这个接口继承自com.sun.source.tree.Tree接口,例如IfTree语法节点表示一个if类型的表达式

每个语法节点都是com.sun.tools.javac.tree.JCTree的子类,并且会实现第一节点中的xxxTree接口类,这个类的名称类似于JCxxx

所有JCxxx 类都作为一个静态内部类定义在JCTree类中

image-20230616134556092

JCTree类有三个重要的属性类

Tree tag :每个语法节点都会用一个整型常数表示,并且每个节点类型的数值都是在前一个的基础上+1,顶层节点TOPLEVEL是1,而IMPORT节点等于TOPLEVEL+1,也就是2

pos :也是一个整数,存储的是这个语法节点在源代码中的起始位置,一个文件的位置是0,-1代表不存在

type :表示这个节点是什么Java类型

例如之前的这个函数

public JCExpression qualident(boolean allowAnnos) {
        JCExpression t = toP(F.at(token.pos).Ident(ident()));
    ...
}

调用了TreeMaker类,根据Name对象构建了一个JCIdent语法节点,如果包名是多级目录,将构建成JCFieldAccess语法节点,此节点也可以是嵌套关系

Package 节点解析完成之后进入while循环,首先解析importDeclaration,解析规则和package类似;解析节点之后构建语法树:

    /** ImportDeclaration = IMPORT [ STATIC ] Ident { "." Ident } [ "." "*" ] ";"
     */
    protected JCTree importDeclaration() {
        int pos = token.pos;
        nextToken();
        boolean importStatic = false;
        //检查是否有static关键字,如果有则设置标识,然后解析第一个类路径,是多级目录则继续读取下一个,并构建JCFiledAccess
        if (token.kind == STATIC) {
            importStatic = true;
            nextToken();
        }
        JCExpression pid = toP(F.at(token.pos).Ident(ident()));
        do {
            int pos1 = token.pos;
            accept(DOT);
            //如果最后一个Token为*,则设置这个JCFieldAccess的Token名称为asterisk
            if (token.kind == STAR) {
                pid = to(F.at(pos1).Select(pid, names.asterisk));
                nextToken();
                break;
            } else {
                pid = toP(F.at(pos1).Select(pid, ident()));
            }
        } while (token.kind == DOT);
        accept(SEMI);
        //最后将这个解析的语法节点作为子结点构建在新创建的JCImport节点中
        return toP(F.at(pos).Import(pid, importStatic));
    }

JCImport语法树如图:

image-20230616211036407

Import节点解析完成之后就是class的解析,类包括interface、class、enum,以class为例:

    /** ClassDeclaration = CLASS Ident TypeParametersOpt [EXTENDS Type]
     *                     [IMPLEMENTS TypeList] ClassBody
     *  @param mods    The modifiers starting the class declaration
     *  @param dc       The documentation comment for the class, or null.
     */
    protected JCClassDecl classDeclaration(JCModifiers mods, Comment dc) {
    	//第一个token是这个类的关键词
        int pos = token.pos;
        accept(CLASS);
        Name name = typeName();
//这个类的类型可选参数,将这个参数解析为JCTypeParameter语法节点
        List<JCTypeParameter> typarams = typeParametersOpt();

        JCExpression extending = null;
        if (token.kind == EXTENDS) {
            nextToken();
            extending = parseType();
        }
        List<JCExpression> implementing = List.nil();
        if (token.kind == IMPLEMENTS) {
            nextToken();
            implementing = typeList();
        }
        //对classBody的解析,也是按照变量定义解析、方法定义解析和内部类定义解析,结果保存在list集合
        List<JCExpression> permitting = permitsClause(mods, "class");
        List<JCTree> defs = classInterfaceOrRecordBody(name, false, false);
//最后将这些子节点添加到JCClassDecl这课class树种
        JCClassDecl result = toP(F.at(pos).ClassDef(
            mods, name, typarams, extending, implementing, permitting, defs));
        attach(result, dc);
        return result;
    }

例如这一段代码:

public class YuFa {
    int a;
    private int c = a + 1;
    
    public int getC() {
        return c;
    }
    
    public void setC(int c) {
        this.c = c;
    }
}

这段代码对应的语法树:

image-20230616211648999

当这个类解析完成之后,会将这个类节点加到这个类对应的包路径的顶层节点也就是JCCompilationUnit,它持有以package 作为pid和JCClassDecl 的集合,这样整个.java文件就被解析完成

所举例代码对应的完整语法树:

image-20230616211929240

注意:所有语法节点的生成都是在TreeMaker类中完成,它实现了在JCTree.Factory 接口中定义的所有节点的构成方法

语义分析器

作用:处理语法树,例如添加默认的构造函数

类:com.sun.tools.javac.comp.Enter

主要完成以下两个步骤

将所有类中出现的符号输入到类自身的符号表中,所有类符号、类的参数类型符号(泛型参数类型)、超类符号和继承的接口类型符号等都存储到一个未处理的列表中

将这个未处理列表中所有的类都解析到各自的类符号列表中,这个操作是在MemberEnter.complete()中完成的

下一个步骤是处理annotation,这个步骤是由com.sun.tools.processing.JavacProssessingEnvironment完成的

再接下来是com.sun.tools.javac.comp.Attr,最重要的是检查语义的合法性并进行逻辑判断,如以下几点:

  • 变量的类型是否匹配
  • 变量在使用前是否已经完成初始化
  • 能够推导出泛型方法的参数类型
  • 字符串常量的合并

在这个步骤中除 Atr 之外还需要另外一些类来协助,如下所述。

  • com.sun.tools.javac.comp.Check:辅助 Attr 类检查语法树中的变量类型是否正确,如二元操作符两边的操作数的类型是否匹配,方法返回的类型是否与接收的引用值类型匹配等。
  • com.sun.tools.javac.comp. Resolve: 主要检查变量、方法或者类的访问是否合法、变量是否是静态变量、变量是否已经初始化等。
  • com.sun.tools.javac.comp.ConstFold:常量折叠,这里主要针对字符串常量,会将一个字符串常量中的多个宇符串合并成一个字符串。
  • com.sun.tools.javac.comp.Infer:帮助推导泛型方法的参数类型等

标注完成后由 com.sun.tools.javac.comp.Flow 类完成数据流分析,数据流分析主要完成如下工作:

  1. 检查变量在使用前是否都己经被正确赋值。除了 Java 中的原始类型,如 int.long、byte、double、char、float,都会有默认的初始化值,其他像String 类型和对象的引用都必须在使用前先赋值。
  2. 保证 final 修饰的变量不会被重复赋值。经过final 修饰的变量只能赋一次值,重复赋值会在这一步编译时报错,如果这个变量是静态变量,则在定义时就必须对其赋值。
  3. 要确定方法的返回值类型。这里需要检查方法的返回值类型是否确定,并检查接受这个方法返回值的引用类型是否匹配,如果没有返回值,则不能有任何引用类型指向方法的这个返回值。
  4. 所有的 Checked Exception 都要捕获或者向上抛出。例如,我们使用 FilelnputStream读取一个文件时,必须捕获可能抛出的FileNotFondException 异常,或者直接向上层方法抛出这个异常。
  5. 所有的语句都要被执行到。这里会检查是否有语句出现在一个return 方法的后面,因为在 return 方法后面的语句永远也不会被执行到。

语法分析的最后一步是执行com.sun.tools.javac.comp.Flow,这是在进一步对语法树进行语义分析,如消除一些无用的代码,总结:

  • 去掉无用的代码
  • 变量的自动转换
  • 去除语法糖

代码生成器

经过语义分析器完成后的语法树已经非常完善了,接下来javac会调用com.sun.tools.javac.jvm.Gen类遍历语法树,生成最终的Java字节码,主要为两个步骤:

将Java方法中的代码块转化成符合JVM语法的命令形式,JVM的操作是基于栈的,所有操作都必须经过出栈和进栈完成

按照JVM的文件组织格式将字节码输出到以class为拓展名的文件

生成字节码除Gen类之外还有两个重要的辅助类:

Items:这个类表示任何可寻址的操作项,包括本地变量、类实例变量或者常量池中用户自定义的常量,这些操作项都可以作为一个单位出现在操作栈上

不同类型的Item对应不同的JVM的操作码:ImmediateItem(常量类型)、LocalItem(本地变量)、StackItem(栈中元素)

Code:存储生成的字节码,并提供一些能够映射操作码的方法

Gen会以后序遍历的顺序解析语法树,将add方法的方法块JCBlock的代码转换成JVM对应的字节码,时序图:

image-20230616222538216

最后使用callMethod方法,返回给方法的调用者

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

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

相关文章

阿里云服务器价格如何?与其他云服务提供商的价格对比如何?

阿里云服务器价格如何&#xff1f;与其他云服务提供商的价格对比如何&#xff1f;   阿里云服务器价格概述   作为全球领先的云计算服务提供商&#xff0c;阿里云在确保服务器性能和安全性的同时&#xff0c;也非常注重产品的价格竞争力。阿里云服务器&#xff08;ECS&…

OpenMMLab-AI实战营第二期——6-2.玩转AIGC神器MMagic

文章目录 1. 基本介绍2. 动手实验 视频链接&#xff1a;玩转AIGC神器MMagic 代码教程&#xff1a;https://github.com/TommyZihao/MMagic_Tutorials 1. 基本介绍 Stable Diffusion的文生图&#xff0c;以及controlnet和dreamboost的图生图&#xff08;输入一个图像文字描述&am…

python Django web 项目 联通用户管理系统

文章目录 1框架MVC 介绍Django 框架的介绍基础命令Django处理浏览器的请求的流程 部门表部门表显示7.模板的继承部门表的添加部门表的删除request.POST.get(‘key’) 、 request.GET.get(key, )部门表的编辑filter() 得到可迭代的QuerySet对象,支持for循环取容器内的元素first(…

图神经网络 GNN 入门

参考链接 A Gentle Introduction to Graph Neural Networks (distill.pub) 零基础多图详解图神经网络&#xff08;GNN/GCN&#xff09;【论文精读】_哔哩哔哩_bilibili 目录 图的基本构成 图的表示方法 图的示例 图网络的基本任务 图网络的处理 影响图网络效果的超参数 …

机器人参数化建模与仿真,软体机器人

专题一&#xff1a;机器人参数化建模与仿真分析、优化设计专题课程大纲 机器人建模基础 机器人运动学基础几何运动学闭环解解析法建模运动学MATLAB脚本文件编写&#xff08;封闭解、构型绘制&#xff09;、工具箱机器人工作空间&#xff08;离散法、几何法&#xff09;建模工作…

客户自助服务第一步:在线客服、在线帮助中心

随着互联网的快速发展&#xff0c;越来越多的企业开始重视客户体验&#xff0c;不断提升客户服务水平。其中&#xff0c;客户自助服务是提高客户满意度的重要途径之一。本文将从在线客服和在线帮助中心两个方面介绍。 客户自助服务的第一步 一、在线客服 在线客服是指企业通…

【linux】探索Linux命令行中强大的网络工具:netstat

文章目录 前言一、netstat是什么&#xff1f;二、使用方法1.常用参数2.实例演示3.更多功能 总结 前言 在Linux命令行中&#xff0c;有许多实用的工具可帮助我们管理和监控网络连接。其中一个最重要的工具就是netstat&#xff0c;它提供了丰富的网络连接和统计信息&#xff0c;…

怎么压缩PDF文件,这三个方便帮你一键压缩!

PDF文件是一种广受欢迎的版式文件格式&#xff0c;由Adobe公司发明&#xff0c;具有高度的兼容性&#xff0c;无论在不同的软件和设备中打开&#xff0c;都不会影响页面的排版。如今&#xff0c;人们常常上网搜索并下载需要的资料&#xff0c;例如电子书和PPT模板&#xff0c;这…

合宙Air724UG Cat.1模块硬件设计指南--USB接口

USB接口 简介 USB (Universal Serial Bus&#xff0c;通用串行总线) 是一种新兴的并逐渐取代其他接口标准的数据通信方式&#xff0c;自推出以来&#xff0c;已成功替代串口和并口&#xff0c;成为21世纪大量计算机和智能设备的标准扩展接口和必备接口之一&#xff0c;USB 具有…

HarmonyOS学习路之开发篇—Java UI框架(JS FA调用Java PA)

JS FA调用Java PA机制 使用兼容JS的类Web开发范式的方舟开发框架提供了JS FA&#xff08;Feature Ability&#xff09;调用Java PA&#xff08;Particle Ability&#xff09;的机制&#xff0c;该机制提供了一种通道来传递方法调用、处理数据返回以及订阅事件上。 当前提供Ab…

从零开发短视频电商 Jmeter插件安装和常用插件

Jmeter插件安装和常用插件 插件安装方式 一种是手动安装各种插件&#xff0c;下载对应的jar包&#xff0c;放到lib\ext目录下就可以使用了。另一种是通过漂亮的 UI &#xff0c;jmeter 插件管理器Plugins Manager可以方便的管理其他插件的下载和更新。安装一次插件管理器&…

1设计模式

面试专题-设计模式 前言 在平时的开发中&#xff0c;涉及到设计模式的有两块内容&#xff0c;第一个是我们平时使用的框架&#xff08;比如spring、mybatis等&#xff09;&#xff0c;第二个是我们自己开发业务使用的设计模式。 面试官一般比较关心的是你在开发过程中&#…

服务器性能扩展后,重启EasyDSS但无法运行是什么原因?

EasyDSS支持一站式的上传、转码、直播、回放、嵌入、分享功能&#xff0c;具有多屏播放、自由组合、接口丰富等特点。平台可以为用户提供专业、稳定的直播推流、转码、分发和播放服务&#xff0c;全面满足超低延迟、超高画质、超大并发访问量的要求。在推流方面&#xff0c;Eas…

seldom 实战技巧

seldom 是我一直在维护的自动化测试框架。目前GitHub已经 500 star。 最近在项目中使用Seldom poium 编写自动化测试用例。接下来&#xff0c;我就分享一些使用技巧。 如何参数化测试用例 网站的首页&#xff0c;如上面的导航。于是&#xff0c;开始利用poium编写元素定位。…

进程间通信 + 消息队列

进程间通信 每个进程有各自不同的用户地址空间&#xff0c;任何一个进程的全局变量在另一个进程中都看不到&#xff0c;所以进程之间要交换数据必须通过内核&#xff0c;在内核中开辟一块缓冲区&#xff0c;进程1吧数据从用户空间考到内核缓冲区&#xff0c;进程2再从内核缓冲…

android studio自带手机投屏功能

android studio自带手机投屏功能 最新版的android studio自带有手机投屏功能&#xff0c;设置后直接在android studio里面就可以“实时”投屏并操控手机。 &#xff08;1&#xff09;File - Settings - Experimental &#xff0c;打开android物理实体设备镜像开关&#xff1a;…

C语言之指针详解(8)

目录 本章重点 1. 字符指针 2. 数组指针 3. 指针数组 4. 数组传参和指针传参 5. 函数指针 6. 函数指针数组 7. 指向函数指针数组的指针 8. 回调函数 9. 指针和数组面试题的解析 指针和数组笔试题解析 #include<stdio.h> int main() {//一维数组int a[] { 1,2,…

ABB机器人与西门子IO通讯

ABB与西门子 Profinet IO通讯 &#xff08;888-3&#xff09; 设定步骤&#xff1a; ABB 1、IP地址 &#xff1a;192.168.0.2 IPsetting 2、站名 ABB Industrial Network -->PROFINET 修改站名 3、字节大小 8字节 PROFINET InternalDevice 4、发送接受区域 sign GO1 组输出1…

CVE-2023-0386:Overlay 文件系统 copy-up 本地提权漏洞分析

漏洞公告 [影响范围] Linux 内核版本&#xff1a;v5.11-rc1 ~ v6.2-rc5 [漏洞描述] A flaw was found in the Linux kernel, where unauthorized access to the execution of the setuid file with capabilities was found in the Linux kernel’s OverlayFS subsystem in h…

【备战秋招】每日一题:5月13日美团春招第二题:题面+题目思路 + C++/python/js/Go/java带注释

为了更好的阅读体检&#xff0c;可以查看我的算法学习博客第二题-南北对决 https://codefun2000.com/p/P1138 在线评测链接:P1287 题目描述 南派北派武林大会开始了。本次攻擂赛有 n 名武者参加&#xff0c;其中按顺序第 i 名武者获得战斗力属性为 i 。每名武者分来自南派或…