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

Go 代码块与作用域,变量遮蔽问题详解

Go 代码块与作用域详解

文章目录

  • Go 代码块与作用域详解
    • 一、引入
    • 二、代码块 (Block)
      • 2.1 代码块介绍
      • 2.2 显式代码块
      • 2.3 隐式代码块
      • 2.4 空代码块
      • 2.5 支持嵌套代码块
    • 三、作用域 (Scope)
      • 3.1 作用域介绍
      • 3.2 作用域划定原则
      • 3.3 标识符的作用域范围
        • 3.3.1 预定义标识符作用域
        • 3.3.2 包代码块级作用域
        • 3.3.3 文件代码块作用域(包的导入作用域)
        • 3.3.4 函数体的作用域
        • 3.3.5 流程控制作用域
    • 四、避免变量遮蔽的原则
      • 4.1 变量遮蔽的根本原因
      • 4.2 变量遮蔽问题分析
        • 4.2.1 第一个问题:遮蔽预定义标识符
        • 4.2.2 第二个问题:遮蔽包代码块中的变量
        • 4.2.3 第三个问题:遮蔽外层显式代码块中的变量
    • 五、利用工具检测变量遮蔽问题

一、引入

首先我们从一个 Go 变量遮蔽(Variable Shadowing)的问题说起。

什么是变量遮蔽呢?

变量遮蔽(Variable Shadowing)是指在程序中一个作用域内的变量名(或标识符)隐藏(遮蔽)了外部作用域中相同名称的变量。这会导致在遮蔽内部作用域内,无法直接访问外部作用域的变量,因为编译器或解释器将优先选择内部作用域的变量,而不是外部的。

我们来看下面这段示例代码:

package mainimport "fmt"var x = 10 // 包级作用域的变量func main() {x := 5 // 函数内的局部变量,遮蔽了包级作用域的 xfmt.Println(x) // 输出:5
}func anotherFunction() {fmt.Println(x) // 在这个函数中,外部包级作用域的 x 是可见的,输出:10
}

你可以看到,在这段代码中,函数main内部有一个局部变量 x,它遮蔽了包级作用域的 x。因此,在main函数内部,通过变量 x 访问的是局部变量,而不是外部包级作用域的变量。然而,在anotherFunction中,没有局部变量 x,因此外部包级作用域的 x 是可见的。

二、代码块 (Block)

2.1 代码块介绍

在Go语言中,代码块是包裹在一对大括号{} 包围的声明和语句序列。

2.2 显式代码块

这些代码块是你在代码中明确可见的,由一对大括号 {} 包围。比如函数的函数体、for循环的循环体、以及其他控制结构内部的代码块。这些代码块明确定义了它们的作用域,包括变量的可见性:

func Foo() {// 这里是显式代码块,包裹在函数的函数体内// ...for {// 这里是显式代码块,包裹在for循环体内// 该代码块也是嵌套在函数体显式代码块内部的代码块// ...}if true {// 这里是显式代码块,包裹在if语句的true分支内// 该代码块也是嵌套在函数体显式代码块内部的代码块// ...}
}

2.3 隐式代码块

隐式代码块没有显式代码块那样的肉眼可见的配对大括号包裹,我们无法通过大括号来识别隐式代码块。

虽然隐式代码块身着“隐身衣”,但我们也不是没有方法来识别它,因为 Go 语言规范对现存的几类隐式代码块做了明确的定义,我们可以看下这张图:

WechatIMG221

我们按代码块范围从大到小,逐一说明:

  • 宇宙(Universe)代码块:它囊括的范围最大,所有 Go 源码都在这个隐式代码块中,你也可以将该隐式代码块想象为在所有 Go 代码的最外层加一对大括号,就像图中最外层的那对大括号那样。
  • 包代码块:在宇宙代码块内部嵌套了包代码块(Package Block),每个 Go 包都对应一个隐式包代码块,每个包代码块包含了该包中的所有 Go 源码,不管这些代码分布在这个包里的多少个的源文件中。
  • 文件代码块:在包代码块的内部嵌套着若干文件代码块(File Block),每个 Go 源文件都对应着一个文件代码块,也就是说一个 Go 包如果有多个源文件,那么就会有多个对应的文件代码块。
  • 再下一个级别的隐式代码块就在控制语句层面了,包括 ifforswitch。我们可以把每个控制语句都视为在它自己的隐式代码块里。不过你要注意,这里的控制语句隐式代码块与控制语句使用大括号包裹的显式代码块并不是一个代码块。你再看一下前面的图,switch 控制语句的隐式代码块的位置是在它显式代码块的外面的。
  • 最后,位于最内层的隐式代码块是 switchselect 语句的每个 case/default 子句中,虽然没有大括号包裹,但实质上,每个子句都自成一个代码块。

2.4 空代码块

如果一对大括号内部没有任何声明或其他语句,我们就把它叫做空代码块

空代码块在Go语言中是有效的,并且在某些情况下可以有一定的用途,尤其是在控制结构中,如if语句、for循环或switch语句的特定分支。它们充当了占位符,允许你将来添加代码而不需要改变代码的结构。

以下是一个示例,演示了空代码块的使用:

func main() {x := 10if x > 5 {// 非空代码块fmt.Println("x 大于 5")} else {// 空代码块,什么都不做}for i := 0; i < 5; i++ {// 空代码块,什么都不做}
}

2.5 支持嵌套代码块

Go 代码块支持嵌套,我们可以在一个代码块中嵌入多个层次的代码块,如下面示例代码所示:

func foo() { //代码块1{ // 代码块2{ // 代码块3{ // 代码块4}}}
}

三、作用域 (Scope)

3.1 作用域介绍

作用域的概念是针对标识符的,不局限于变量。每个标识符都有自己的作用域,而一个标识符的作用域就是指这个标识符在被声明后可以被有效使用的源码区域。

显然,作用域是一个编译期的概念,也就是说,编译器在编译过程中会对每个标识符的作用域进行检查,对于在标识符作用域外使用该标识符的行为会给出编译错误的报错。

3.2 作用域划定原则

我们可以使用代码块的概念来划定每个标识符的作用域。一般划定**原则就是声明于外层代码块中的标识符,其作用域包括所有内层代码块。**而且,这一原则同时适于显式代码块与隐式代码块。

3.3 标识符的作用域范围

3.3.1 预定义标识符作用域

首先,我们来看看位于最外层的宇宙隐式代码块的标识符。这一区域是 Go 语言预定义标识符的自留地。你可以看看下面这张表是Go 语言当前版本定义里的所有预定义标识符:

WechatIMG222

由于这些预定义标识符位于包代码块的外层,所以它们的作用域是范围最大的,对于开发者而言,它们的作用域就是源代码中的任何位置。不过,这些预定义标识符不是关键字,我们同样可以在内层代码块中声明同名的标识符。

3.3.2 包代码块级作用域

包顶层声明中的常量、类型、变量或函数(不包括方法)对应的标识符的作用域是包代码块。

不过,对于作用域为包代码块的标识符,我需要你知道一个特殊情况。那就是当一个包 A 导入另外一个包 B 后,包 A 仅可以使用被导入包包 B 中的导出标识符(Exported Identifier)。

按照 Go 语言定义,一个标识符要成为导出标识符需同时具备两个条件:一是这个标识符声明在包代码块中,或者它是一个字段名或方法名;二是它名字第一个字符是一个大写的 Unicode 字符。这两个条件缺一不可。

// 包 A
package Aimport "B"func SomeFunction() {// 可以访问包 B 中的导出标识符B.ExportFunction()
}// 这里无法访问包 B 中的非导出标识符
3.3.3 文件代码块作用域(包的导入作用域)

在Go语言中,除了大多数在包顶层声明的标识符具有包代码块范围的作用域外,还有一个特殊情况,即导入的包名。导入的包名的作用域是文件代码块范围,这意味着它在包含它的源代码文件中可见,但对其他源文件不可见。

考虑以下示例,其中一个包A有两个源文件,它们都依赖包B中的标识符:

// 文件1:source1.gopackage Aimport "B"func FunctionInSource1() {B.SomeFunctionFromB() // 可以使用导入的包名 B
}
// 文件2:source2.gopackage Aimport "B"func FunctionInSource2() {B.AnotherFunctionFromB() // 可以使用导入的包名 B
}

在这个示例中,两个源文件都导入了包B,但每个文件内的包名 B文件级别可见。这意味着FunctionInSource1FunctionInSource2函数都可以访问B包中的导出标识符(以大写字母开头的标识符),但对于其他包和源文件而言,它们不可见。

3.3.4 函数体的作用域

函数体内的标识符的作用域被限制在函数的开始和结束之间。这意味着函数体内的局部变量只能在函数体内部访问。

func exampleFunction() {var localVar = 42fmt.Println(localVar) // 可以访问局部变量 localVar
}fmt.Println(localVar) // 这里无法访问局部变量 localVar
3.3.5 流程控制作用域

流程控制结构,如if语句、for循环和switch语句,也会引入新的作用域。在这些结构中声明的局部变量的作用域限制在结构内部,不会泄漏到外部。

if x := 10; x > 5 {// x 只能在 if 语句块内访问fmt.Println(x)
}fmt.Println(x) // 这里无法访问 x

在上面的示例中,变量 x 在if语句内部有一个新的局部作用域,因此它只在if语句块内可见。

四、避免变量遮蔽的原则

4.1 变量遮蔽的根本原因

变量是标识符的一种,通过以上我们知道,一个变量的作用域起始于其声明所在的代码块,并且可以一直扩展到嵌入到该代码块中的所有内层代码块,而正是这样的作用域规则,成为了滋生“变量遮蔽问题”的土壤。

变量遮蔽问题的根本原因,就是内层代码块中声明了一个与外层代码块同名且同类型的变量,这样,内层代码块中的同名变量就会替代那个外层变量,参与此层代码块内的相关计算,我们也就说内层变量遮蔽了外层同名变量。现在,我们先来看一下这个示例代码,它就存在着多种变量遮蔽的问题:

... ...var a int = 2020func checkYear() error {err := errors.New("wrong year")switch a, err := getYear(); a {case 2020:fmt.Println("it is", a, err)case 2021:fmt.Println("it is", a)err = nil}fmt.Println("after check, it is", a)return err}type new intfunc getYear() (new, error) {var b int16 = 2021return new(b), nil}func main() {err := checkYear()if err != nil {fmt.Println("call checkYear error:", err)return}fmt.Println("call checkYear ok")}

这个变量遮蔽的例子还是有点复杂的,我们首先运行一下这个例子:

$go run complex.go
it is 2021
after check, it is 2020
call checkYear error: wrong year

我们可以看到,第 20 行定义的 getYear 函数返回了正确的年份 (2021),但是 checkYear 在结尾却输出“after check, it is 2020”,并且返回的 err 并非为 nil,这显然是变量遮蔽的“锅”!

根据我们前面给出的变量遮蔽的根本原因,看看上面这段代码究竟有几处变量遮蔽问题(包括标识符遮蔽问题)。

4.2 变量遮蔽问题分析

4.2.1 第一个问题:遮蔽预定义标识符

面对上面代码,我们一眼就看到了位于第 18 行的 new,这本是 Go 语言的一个预定义标识符,但上面示例代码呢,却用 new 这个名字定义了一个新类型,于是 new 这个标识符就被遮蔽了。如果这个时候你在 main 函数下方放上下面代码:

p := new(int)
*p = 11

你就会收到 Go 编译器的错误提示:“type int is not an expression”,如果没有意识到 new 被遮蔽掉,这个提示就会让你不知所措。不过,在上面示例代码中,遮蔽 new 并不是示例未按预期输出结果的真实原因,我们还得继续往下看。

4.2.2 第二个问题:遮蔽包代码块中的变量

你看,位于第 7 行的 switch 语句在它自身的隐式代码块中,通过短变量声明形式重新声明了一个变量 a,这个变量 a 就遮蔽了外层包代码块中的包级变量 a,这就是打印“after check, it is 2020”的原因。包级变量 a 没有如预期那样被 getYear 的返回值赋值为正确的年份 2021,2021 被赋值给了遮蔽它的 switch 语句隐式代码块中的那个新声明的 a。

4.2.3 第三个问题:遮蔽外层显式代码块中的变量

同样还是第 7 行的 switch 语句,除了声明一个新的变量 a 之外,它还声明了一个名为 err 的变量,这个变量就遮蔽了第 4 行 checkYear 函数在显式代码块中声明的 err 变量,这导致第 12 行的 nil 赋值动作作用到了 switch 隐式代码块中的 err 变量上,而不是外层 checkYear 声明的本地变量 err 变量上,后者并非 nil,这样 checkYear 虽然从 getYear 得到了正确的年份值,但却返回了一个错误给 main 函数,这直接导致了 main 函数打印了错误:“call checkYear error: wrong year”。

通过这个示例,我们也可以看到,短变量声明与控制语句的结合十分容易导致变量遮蔽问题,并且很不容易识别,因此在日常 go 代码开发中你要尤其注意两者结合使用的地方。

五、利用工具检测变量遮蔽问题

依靠肉眼识别变量遮蔽问题终归不是长久之计,所以Go 官方提供了 go vet 工具可以用于对 Go 源码做一系列静态检查,在 Go 1.14 版以前默认支持变量遮蔽检查,Go 1.14 版之后,变量遮蔽检查的插件就需要我们单独安装了,安装方法如下:

go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow@latest

安装成功后,我们就可以通过 go vet 扫描代码并检查这里面有没有变量遮蔽的问题了。我们检查一下前面的示例代码,看看效果怎么样。执行检查的命令如下:

$go vet -vettool=$(which shadow) -strict complex.go 
./complex.go:13:12: declaration of "err" shadows declaration at line 11

相关文章:

Go 代码块与作用域,变量遮蔽问题详解

Go 代码块与作用域详解 文章目录 Go 代码块与作用域详解一、引入二、代码块 (Block)2.1 代码块介绍2.2 显式代码块2.3 隐式代码块2.4 空代码块2.5 支持嵌套代码块 三、作用域 (Scope)3.1 作用域介绍3.2 作用域划定原则3.3 标识符的作用域范围3.3.1 预定义标识符作用域3.3.2 包代…...

可观测性-Metrics-WebClient异步Http远程Call

代码示例 1.依赖导入 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><ar…...

Android之播放本地视频和Url视频方法

一、播放本地视频文件 根据文件路径在浏览器中播放&#xff0c;可用于视频预览等场景 效果&#xff1a; 用浏览器播放本地视频 文件路径例子&#xff1a; /storage/emulated/0/Android/data/com.custom.jfrb/files/Movies/1697687179497.mp4 File file new File("文件…...

设计模式:工厂方法模式(C#、JAVA、JavaScript、C++、Python、Go、PHP):

本节主要介绍设计模式中的工厂方法模式。 简介&#xff1a; 工厂方法模式&#xff0c;它是对简单工厂模式的进一步抽象化&#xff0c;其好处是可以使系统在不修改原来代码的情况下引进新的产品&#xff0c;即满足开闭原则。 它定义了一个用于创建对象的工厂接口&#xff0c;让…...

C++基础——指针

1 概述 指针的作用&#xff1a;可以通过指针间接访问内存 内存编号从0开始&#xff0c;一般使用十六进制数字表示&#xff0c;指针可以保存地址 2 指针变量定义和作用 int main() {//1、指针的定义int a 10; //定义整型变量a//指针定义语法&#xff1a; 数据类型 * 变量名 …...

PLC 学习day02 硬件输入/输入的知识

1.资料来源 1.链接&#xff1a;三菱PLC视频教程全集之FX3U基本单元输入接线_哔哩哔哩_bilibili 2. 链接&#xff1a; 三菱plc视频教程全集之FX3U基本单元输出接线_哔哩哔哩_bilibili 2. PLC 的输入部分器件连接。 2.1 PLC输入部分的硬件知识 1. 一般输入部分是PLC获取信息的地…...

rabbitMq (2)

RabbitMQ 消息应答与发布 文章目录 1. 消息应答1.2 自动应答1.2 手动应答1.3 代码案例 2. RabbitMQ 持久化2.1 队列持久化2.2 消息持久化 3. 不公平分发4. 预取值分发5. 发布确认5.1 发布确认逻辑5.2 开启发布确认的方法5.3 单个确认发布5.4 批量确认发布5.5 异步确认5.5.1 处理…...

通讯协议学习之路:RS422协议理论

通讯协议之路主要分为两部分&#xff0c;第一部分从理论上面讲解各类协议的通讯原理以及通讯格式&#xff0c;第二部分从具体运用上讲解各类通讯协议的具体应用方法。 后续文章会同时发表在个人博客(jason1016.club)、CSDN&#xff1b;视频会发布在bilibili(UID:399951374) 一、…...

剪映failed to initialize,cuda.is_available()为false解决

debug记录帖 错误1&#xff1a;打开剪映发现弹窗提示failed to initialize graphics backed for D3D11 错误2&#xff1a;torch版本、cuda版本&#xff08;之前的正常环境&#xff09;都对但是torch.cuda.is_available()为false 怀疑是显卡驱动的问题 打开Nvidia Geforce Exp…...

基于Spring Boot的LDAP开发全教程

写在前面 协议概述 LDAP&#xff08;轻量级目录访问协议&#xff0c;Lightweight Directory Access Protocol)是一种用于访问和维护分布式目录服务的开放标准协议,是一种基于TCP/IP协议的客户端-服务器协议&#xff0c;用于访问和管理分布式目录服务&#xff0c;如企业内部的…...

在 Linux 上保护 SSH 服务器连接的 8 种方法

SSH 是一种广泛使用的协议&#xff0c;用于安全地访问 Linux 服务器。大多数用户使用默认设置的 SSH 连接来连接到远程服务器。但是&#xff0c;不安全的默认配置也会带来各种安全风险。 具有开放 SSH 访问权限的服务器的 root 帐户可能存在风险。尤其是如果使用的是公共 IP 地…...

摩尔信使MThings的协议转换(数据网关)功能

摩尔信使MThings可以作为现场总线&#xff08;RS485&#xff09;和以太网的数据中枢&#xff0c;并拥有强大的Modbus协议转换功能。 数据网关功能提供协议转换和数据汇聚功能&#xff0c;可实现多维度映射&#xff0c;包括&#xff1a;不同的通道(总线)类型、协议类型&#xff…...

Mac安装Kali保姆级教程

Mac安装Kali保姆级教程 其他安装教程&#xff1a;使用VMware安装系统Window、Linux&#xff08;kali&#xff09;、Mac操作系统 1 虚拟机安装VM Fusion 去官网下载VM Fusion 地址&#xff1a;https://customerconnect.vmware.com/en/evalcenter?pfusion-player-personal-13 …...

利用Spring Boot框架做事件发布和监听

一、编写事件 1.编写事件类并集成spring boot 事件接口&#xff0c;提供访问事件参数属性 public class PeriodicityRuleChangeEvent extends ApplicationEvent {private final JwpDeployWorkOrderRuleDTO jwpDeployWorkOrderRuleDTO;public PeriodicityRuleChangeEvent(Obje…...

KingBase库模式表空间和客户端认证(kylin)

库、模式、表空间 数据库 数据库基集簇与数据库实例 KES集簇是由单个KES实例管理的数据库的集合KES集簇中的库使用相同的全局配置文件和监听端口、共享相关的进程和内存结构同一数据库集簇中的进程、相关的内存结构统称为实例 数据库 数据库是一个长期存储在计算机内的、有…...

h5的扫一扫功能 (非微信浏览器环境下)

必须在 https 域名下才生效 <template><div><van-field label"服务商编码" right-icon"scan" placeholder"扫描二维码获取" click-right-icon"getCameras" /> <div class"scan" :style"{disp…...

Typora 导出PDF 报错 failed to export as pdf. undefined 解决方案

情况 我想把一个很大的markdown 导出为 248页的pdf 然后就报错 failed to export as pdf. undefined 原因 &#xff1a; 个人感觉应该是图片太大了 格式问题之类导致的 解决 文件 -> 偏好设置 - > 导出 -> pdf -> 自定义 -> 把大小全部改为24mm (虽然图中是32 …...

[架构之路-239]:目标系统 - 纵向分层 - 中间件middleware

目录 前言&#xff1a; 一、中间件概述 1.1 中间件在软件层次中的位置 1.2 什么是中间件 1.3 为什么需要中间件 1.4 中间件应用场合&#xff08;应用程序不用的底层需求&#xff1a;计算、存储、通信&#xff09; 1.5 中间件分类 - 按内容分 二、嵌入式系统的中间件 2…...

javascript利用xhr对象实现http流的comet轮循,主要是利用readyState等于3的特点

//此文件 为前端获取http流 <!DOCTYPE html> <html xmlns"http://www.w3.org/1999/xhtml" lang"UTF-8"></html> <html><head><meta http-equiv"Content-Type" content"text/html; charsetUTF-8"/&g…...

【Mybatis源码】XPathParser解析器

XPathParser是Mybatis中定义的进行解析XML文件的类,此类用于读取XML文件中的节点文本与属性;本篇我们主要介绍XPathParser解析XML的原理。 一、XPathParser构造方法 这里我们介绍主要的构造方法 public XPathParser(InputStream inputStream, boolean validation, Propert…...

TDengine 快速体验(Docker 镜像方式)

简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能&#xff0c;本节首先介绍如何通过 Docker 快速体验 TDengine&#xff0c;然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker&#xff0c;请使用 安装包的方式快…...

定时器任务——若依源码分析

分析util包下面的工具类schedule utils&#xff1a; ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类&#xff0c;封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz&#xff0c;先构建任务的 JobD…...

生成 Git SSH 证书

&#x1f511; 1. ​​生成 SSH 密钥对​​ 在终端&#xff08;Windows 使用 Git Bash&#xff0c;Mac/Linux 使用 Terminal&#xff09;执行命令&#xff1a; ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" ​​参数说明​​&#xff1a; -t rsa&#x…...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)

设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile&#xff0c;新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...

反射获取方法和属性

Java反射获取方法 在Java中&#xff0c;反射&#xff08;Reflection&#xff09;是一种强大的机制&#xff0c;允许程序在运行时访问和操作类的内部属性和方法。通过反射&#xff0c;可以动态地创建对象、调用方法、改变属性值&#xff0c;这在很多Java框架中如Spring和Hiberna…...

汇编常见指令

汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX&#xff08;不访问内存&#xff09;XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...

全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比

目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec&#xff1f; IPsec VPN 5.1 IPsec传输模式&#xff08;Transport Mode&#xff09; 5.2 IPsec隧道模式&#xff08;Tunne…...

安卓基础(aar)

重新设置java21的环境&#xff0c;临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的&#xff1a; MyApp/ ├── app/ …...

Yolov8 目标检测蒸馏学习记录

yolov8系列模型蒸馏基本流程&#xff0c;代码下载&#xff1a;这里本人提交了一个demo:djdll/Yolov8_Distillation: Yolov8轻量化_蒸馏代码实现 在轻量化模型设计中&#xff0c;**知识蒸馏&#xff08;Knowledge Distillation&#xff09;**被广泛应用&#xff0c;作为提升模型…...

LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf

FTP 客服管理系统 实现kefu123登录&#xff0c;不允许匿名访问&#xff0c;kefu只能访问/data/kefu目录&#xff0c;不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...