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

Go学习第五章——函数与包

Go学习第五章——函数与包

      • 1 函数
        • 1.1 基本语法
        • 1.2 函数多返回值
        • 1.3 函数的可见性和包级函数
        • 1.4 函数调用机制底层原理
        • 1.5 值类型和引用类型
        • 1.6 注意事项和细节
        • 1.7 逃逸机制(补,可不看)
      • 2 包
        • 2.1 快速入门
        • 2.2 包的使用细节
      • 3 函数详细讲解
        • 3.1 递归调用
        • 3.2 可变函数参数
        • 3.3 init 函数
        • 3.4 匿名函数
        • 3.5 闭包
        • 3.6 函数作为参数和返回值
        • 3.7 defer函数
        • 3.8 作为结构体的方法
      • 4 变量的作用域

1 函数

函数是一段可以重复执行的代码块,通过函数可以将代码模块化,提高代码的可读性和可维护性。

要定义函数,需要指定函数的名称、参数和返回值(如果有的话)。

1.1 基本语法

基本语法

func   函数名(形参列表)(返回值类型列表){执行语句..return + 返回值列表
}

下面是一个简单的示例,展示了如何定义和调用一个简单的函数:

package mainimport "fmt"// 定义一个名为greeting的函数,它接收一个字符串参数name并没有返回值
func greeting(name string) {fmt.Printf("Hello, %s!\n", name)
}func main() {// 调用greeting函数,传入一个名字作为参数greeting("Alice")
}

运行上面的代码,输出结果为:

Hello, Alice!
1.2 函数多返回值

在 Go 语言中,函数可以返回多个值。这在某些情况下很有用,例如一个函数需要返回多个计算结果,或者需要返回一个值和一个错误状态。

下面是一个示例,展示了如何定义和使用返回多个值的函数:

package mainimport "fmt"// 定义一个名为divide的函数,它接收两个整数参数,并返回一个商和余数
func divide(a, b int) (int, int) {quotient := a / bremainder := a % breturn quotient, remainder
}func main() {// 调用divide函数,并接收两个返回值q, r := divide(10, 3)fmt.Printf("商:%d,余数:%d\n", q, r)
}

运行上面的代码,输出结果为:

商:3,余数:1

通过返回多个值,函数的调用方可以方便地获得函数计算的多个结果。

1.3 函数的可见性和包级函数

在 Go 语言中,函数和变量的可见性是由它们的命名规则决定的。一个函数或变量是否对其他代码可见,取决于它们的名称是否以大写字母开头

如果一个函数或变量的名称以大写字母开头,则它对其他代码可见;如果名称以小写字母开头,则它只对同一个包内的代码可见。

下面是一个示例,展示了可见性的规则:

package mainimport "fmt"// 可以被其他代码访问
func PublicFunc() {fmt.Println("公有函数")
}// 只能在当前包内访问
func privateFunc() {fmt.Println("私有函数")
}func main() {PublicFunc()privateFunc() // 错误:无法访问私有函数
}

在这个示例中,我们定义了一个名为 PublicFunc 的公有函数,以及一个名为 privateFunc 的私有函数。在 main 函数中,我们可以正常调用 PublicFunc,但无法调用 privateFunc

按照这个规则,我们可以将一些公共的、被其他代码调用的函数定义为包级函数,并将一些内部函数定义为私有函数。这有助于将代码逻辑与实现细节隔离,并提高代码的封装性。

1.4 函数调用机制底层原理
  1. 执行n1 := 10,会生成一个存储这个值的区域,这里只是抽象为有这么一个main栈区,实际上不是这样命名,是使用寄存器和栈帧来实现,具体不讲解
  2. 因为是栈的方式,后进先出,所以这里调用函数后占用的数据,也会被优先回收掉。

在这里插入图片描述

函数调用是计算机程序中的一个重要概念,它用于在程序执行过程中跳转到函数代码的起始位置,并在函数执行完毕后返回到原来的位置。函数调用的底层实现涉及到栈的分配、参数传递和返回值处理等过程。

  1. 栈空间的分配:每个线程都会有自己的栈空间,用于存储函数的局部变量、函数参数和返回值。函数调用时,会给调用栈分配一块空间来存储函数执行过程中所需的数据。栈的分配是一个后进先出(LIFO)的过程,即新的函数调用会在栈的顶部分配空间

  2. 参数传递:函数调用需要将参数传递给被调用的函数。参数的传递方式一般分为两种:值传递和引用传递

  • 在值传递中,参数的值会被复制到被调用函数的栈帧中,由于是复制操作,被调函数的修改不会影响到调用函数。

  • 在引用传递中,函数参数是一个指针,传递的是变量在内存中的地址,被调用函数可以通过指针来访问和修改原始数据。

  1. 函数调用过程:当一个函数需要调用另一个函数时,会先将当前函数的执行状态压入栈中,包括返回地址、参数、局部变量等信息。然后跳转到被调用函数的起始位置,执行被调用函数的代码。在被调用函数执行结束后,会将返回值返回给调用函数,在栈中恢复调用函数的执行状态,包括返回地址和栈帧等信息,然后继续执行。

  2. 返回值处理:函数执行完毕后,需要将返回值返回给调用函数。返回值的处理方式和参数传递类似,可以使用值传递或者引用传递。在实际的底层实现中,一般通过寄存器或者栈帧来传递和存储返回值。

总结起来,函数调用的底层实现主要涉及栈空间的分配与释放、参数传递和返回值处理等过程。这些过程是通过寄存器和栈帧来实现的,不同的编程语言和编译器可能会有一些细节上的差异,但基本思想是相通的。

1.5 值类型和引用类型

前面使用的值传输,所以会发现函数并没有修改值,只是修改函数本身的栈空间里的变量值,当然还有相对应,可以直接通过引用类型传递,修改对应的值。

使用传输地址,也就是引用传递的方式,让函数直接修改地址值。

import "fmt"func add1(num int) {num = num + 1fmt.Println("在add1函数里,num=", num)
}func add2(num *int) {*num = *num + 1fmt.Println("在add2函数里,num=", *num)
}func main() {var num int = 2add1(num)fmt.Println("在main函数里,第一次,num=", num)add2(&num)fmt.Println("在main函数里,第二次,num=", num)
}

输出结果:

在add1函数里,num= 3
在main函数里,第一次,num= 2
在add2函数里,num= 3
在main函数里,第二次,num= 3
1.6 注意事项和细节
  1. golang不支持重载(通过其他方式实现)
  2. 在Go中,函数也是一种数据类型,可以赋值给一个变量,则该变量就是一个函数类型的变量了。通过该变量可以对函数调用。
  3. 函数既然是一种数据类型,因此在Go中,函数可以作为形参,并且调用。
  4. 为了简化数据类型定义,Go支持自定义数据类型
  • 基本语法:type 自定义数据类型名 数据类型 // 理解:相当于一个别名
  • 案例:type myInt int // 这是myInt就等价int来使用了
  • 案例:type mySum func(int, int) int // 这是mySum就等价一个函数类型func (int, int) int
import "fmt"type myFunType func(int, int) intfunc myFun(funvar myFunType, num1 int, num2 int) int {return funvar(num1, num2)
}func main() {type myInt intvar num1 myInt = 40// num2 := int(num1) // 报错,因为myInt本质是,不是一个数据类型fmt.Println("num1=", num1)res := myFun(func(i1 int, i2 int) int { // 这里用到了匿名函数,可以去看看后面所说的定义return i1 + i2}, 500, 600)fmt.Println("res=", res)
}

输出:

num1= 40
res= 1100
  1. 支持对函数返回值命名

cal1和cal2的函数是一样的,只是cal2里对返回值命名,这样就可以不用考虑顺序,因为如果是cal1的方式,return的值需要考虑跟返回值类型的顺序一样。

import ("fmt""strconv"
)func cal1(n1 int, n2 int) (int, string) {sum := n1 + n2sub := strconv.FormatInt(int64(n1-n2), 10)return sum, sub
}func cal2(n1 int, n2 int) (sum int, sub string) {sub = strconv.FormatInt(int64(n1-n2), 10)sum = n1 + n2return
}func main() {sum1, sub1 := cal1(2, 2)sum2, sub2 := cal2(2, 2)fmt.Printf("cal1的返回值:%v, %v \n", sum1, sub1)fmt.Printf("cal2的返回值:%v, %v \n", sum2, sub2)
}

输出结果:

cal1的返回值:4, 0
cal2的返回值:4, 0
  1. 使用_标识符,忽略返回值
import "fmt"func cal(n1 int, n2 int) (sum int, sub int) {sum = n1 + n2sub = n1 - n2return
}func main() {res1, _ := cal(10, 20)fmt.Printf("res1 = %d", res1)
}

输出:res1 = 30

1.7 逃逸机制(补,可不看)

逃逸分析是编译器的一种静态分析技术,用于分析程序中的变量是否会逃逸到堆上分配内存。逃逸指的是当一个变量在函数内部分配内存,并且在函数外部被引用时,该变量就会逃逸到堆上。

逃逸分析的作用是优化内存分配和回收,将一部分变量从堆上分配转移到栈上分配,减少堆的压力和垃圾回收的负担。逃逸分析可以减少内存分配的次数,避免频繁的系统调用和锁竞争,提高程序的性能和并发能力。

逃逸分析的实现原理可以分为以下几个步骤:

  1. 内联优化:逃逸分析通常是在函数级别上进行的,首先编译器会尝试内联函数。内联优化是将函数的代码插入到调用它的函数中,减少函数调用的开销。内联优化会扩展函数的作用域,使函数内部的变量和参数可以直接访问。这样,一些局部变量就可以在栈上分配,而不是在堆上分配。

  2. 变量分析:逃逸分析会对函数的变量进行分析,判断变量是否逃逸。如果一个变量逃逸,编译器会将其分配在堆上;如果一个变量不逃逸,则可以将其分配在栈上。变量的逃逸分析包括以下情况的判断:

    • 变量是否在函数返回后继续存在;
    • 变量是否被存储到全局变量中,以供其他函数使用;
    • 变量是否被闭包函数引用。
  3. 逃逸分析结果的使用:逃逸分析的结果会被编译器用于指导内存分配器进行内存分配。根据逃逸分析的结果,编译器可以决定将变量分配在栈上还是堆上。对于不逃逸的变量,编译器可以直接在栈上分配内存,避免了堆分配和垃圾回收的开销。

总结起来,逃逸分析是一种优化技术,它使用静态分析的方法判断变量是否会逃逸到堆上分配内存。逃逸分析的作用是减少堆的压力和垃圾回收的负担,提高程序的性能和并发能力。逃逸分析的实现原理包括内联优化、变量分析和逃逸分析结果的使用。

2 包

go的每一个文件都是属于一个包,也就是说go是以包的形式来管理文件和项目目录结构。

2.1 快速入门

包的三大作用:

  1. 区分相同名字的很熟、变量等标识符
  2. 当程序文件很多时,可以很好的管理项目
  3. 控制函数、变量等访问范围,即作用域

首先创建不同文件夹下面的包,再编写好对应的函数,然后import导入,最后调用!

在这里插入图片描述

2.2 包的使用细节
  1. 在给一个文件夹打包时,该包对应一个文件夹,比如这里的是utils文件夹对应的包名就是utils,文件的包名通常和文件所在的文件夹名一致,一般为小写字母。

  2. 当包名过长时,可以给包名取别名,取完之后,之前的就不能用了

在import时,在前面写的代码,就是别名。例如:这里把utils改成util。

package mainimport (util "GoStudy_Day1/Day03/model/utils""fmt"
)func main() {var num int = 2nums := util.Cal(num)fmt.Printf("%v 的平方等于:%v \n", num, nums)
}
  1. 编译一个可执行程序文件,就需要将这个包声明为main,然后实际开发的时候,都是编译成exe文件再运行。

编译可以指定名字和目录,比如:放在bin目录下:go build -o bin/my.exe go_code/project/main

3 函数详细讲解

3.1 递归调用

在函数里,又调用了本身,也就是自己

下面是一个案例和底层栈空间的调用情况

在这里插入图片描述

注意细节:

  1. 执行一个函数时,就会串接一个新的受保护的独立空间(新函数栈,如图所示)
  2. 函数的局部变量是独立的,不会相互影响
  3. 递归必须向退出递归的条件逼近,否则就是无限递归,很容易栈溢出崩溃,实际开发不怎么用
  4. 当一个函数执行完毕或者遇到return,就会返回,遵守谁调用,就将结果返回给谁,同时函数执行完毕就销毁。
3.2 可变函数参数

可变参数函数是指可以接收不定数量参数的函数,这些参数被看作是一个切片。

Go 语言中的可变参数函数使用 ... 表示。

下面是一个示例,展示了如何定义和调用可变参数函数:

package mainimport "fmt"// 定义一个名为sum的函数,它接收任意数量的整数参数,并返回它们的总和
func sum(nums ...int) int {total := 0for _, num := range nums {total += num}return total
}func main() {// 调用sum函数,传入多个整数参数fmt.Println("总和:", sum(1, 2, 3, 4, 5))fmt.Println("总和:", sum(10, 20, 30))
}

运行上面的代码,输出结果为:

总和: 15
总和: 60

在这个示例中,我们定义了一个名为 sum 的函数,它可以接收任意数量的整数参数,并返回它们的总和。在 main 函数中,我们调用了 sum 函数两次,并传递了不同数量的参数。

通过使用可变参数,我们可以简化函数的调用,使之更加灵活。

3.3 init 函数

基本介绍:

每一个源文件都可以包含一个init函数,该函数会在main函数执行前,被Go运行框架调用,也就是说init会在main函数前被调用。

案例说明:

import "fmt"func init() {fmt.Println("main init...")
}func main() {fmt.Println("main...")
}

输出结果:

main init...
main... 

细节讨论:

  1. 如果一个文件同时包含全局变量定义,init函数和main函数,则执行的流程是变量定义->init函数->main函数
  2. init函数最主要的作用,就是完成一些初始化的工作,比如下面的案例:

utils包:

package utilsvar Name string
var Age intfunc init() {Name = "Tom"Age = 100
}

main包

import ("GoStudy_Day1/Day03/model/utils""fmt"
)func main() {fmt.Printf("Name = %v, Age = %v", utils.Name, utils.Age)
}

输出结果:Name = Tom, Age = 100

从过程可以看出,有点像Java定义成员变量,并且给一个默认值的感觉

3.4 匿名函数

匿名函数是一种特殊的函数,它没有函数名,可以直接在其他函数中定义和使用。匿名函数在需要临时定义一段代码,并且这段代码不需要复用时很有用。

下面是示例,展示了如何定义和调用匿名函数:

实例一:

package mainimport "fmt"func main() {// 在main函数内定义一个匿名函数,并立即调用它func() {fmt.Println("这是一个匿名函数!")}()// 将匿名函数赋值给变量,然后进行调用greeting := func(name string) {fmt.Printf("Hello, %s!\n", name)}greeting("Alice")
}

运行上面的代码,输出结果为:

这是一个匿名函数!
Hello, Alice!

实例二:将匿名函数赋给a变量

func main() {a := func(n1 int, n2 int) int {return n1 - n2}res2 := a(10, 10)fmt.Println("res2=", res2)
}

输出结果:res2= 0

实例三:匿名函数赋值给全局变量,那么就成为一个全局匿名函数

这里不写了,跟上面雷同~~~

匿名函数可以用于需要临时定义一段代码的场景,例如在并发编程中,可以将匿名函数传递给协程进行并发执行。

3.5 闭包

闭包是指一个函数捕获并保存了其自身外部作用域的变量的引用。

简单来说,闭包就是一个函数以及它所引用的变量的组合体。

下面是一个示例,展示了如何使用闭包:

package mainimport "fmt"// 定义一个名为counter的函数,返回一个匿名函数
func counter() func() int {count := 0return func() int {count++return count}
}func main() {// 创建一个计数器实例c := counter()// 使用闭包进行计数fmt.Println(c()) // 输出:1fmt.Println(c()) // 输出:2fmt.Println(c()) // 输出:3
}

运行上面的代码,输出结果为:

1
2
3

在这个示例中,我们定义了一个 counter 函数,它返回一个匿名函数。在匿名函数内部,我们定义了一个变量 count,然后在每次调用匿名函数时更新这个变量,并返回它的值。

最佳实践

假设传入一个文件名,设置一个变量作为文件名的后缀,如果这个文件名没有指定的后缀,则自动给这个文件名添加后缀,如果有的话,直接输出。

这里需要使用HasSuffix函数,表示查找该该string有没有指定的后缀

func makeSuffix(suffix string) func(string) string {return func(name string) string {// 如果 name 没有指定后缀,则加上,否则就返回原来的名字if !strings.HasSuffix(name, suffix) {return name + suffix}return name}
}func main() {f := makeSuffix(".jpg")fmt.Println("文件名处理后=", f("winter"))fmt.Println("文件名处理后=", f("winter.jpg"))
}

输出结果:

文件名处理后= winter.jpg
文件名处理后= winter.jpg

虽然也可以用普通函数实现,但是过程太过复杂,所以不用。

通过闭包,我们可以创建一个状态被隐藏的函数,这个函数可以持续地访问和修改它所引用的变量。

ps:闭包还能通过这方式实现协程之间的数据传递,不过需要避免变量共享问题。(以后再讲~~)

3.6 函数作为参数和返回值

在 Go 语言中,函数可以作为参数传递给其他函数,也可以作为函数的返回值。这种能力使得代码更加灵活,可以根据需要将函数与其他函数进行组合。

下面是一个示例,展示了如何将函数作为参数和返回值:

package mainimport "fmt"// 定义一个名为apply的函数,它接收一个函数作为参数,并将参数函数应用到数字5上
func apply(f func(int, int) int) {result := f(5, 10)fmt.Println("应用结果:", result)
}// 定义一个名为add的函数,它接收两个整数并返回它们的和
func add(a, b int) int {return a + b
}func main() {// 将add函数作为参数传递给apply函数apply(add)// 将匿名函数作为参数传递给apply函数apply(func(a, b int) int {return a * b})
}

运行上面的代码,输出结果为:

应用结果: 15
应用结果: 50

在这个示例中,我们定义了一个 apply 函数,它接收一个函数作为参数,并将这个函数应用到数字5和10上。在 main 函数中,我们分别将 add 函数和一个匿名函数作为参数传递给 apply 函数,用于实现加法和乘法操作。

通过将函数作为参数和返回值,我们可以更灵活地组合和使用函数,实现更多复杂的功能。

3.7 defer函数

为什么需要defer

在函数中,程序员进程需要创建资源(比如:数据库连接、文件句柄、锁等),为了在函数执行完毕后,及时的释放资源,Go的设计者提供defer(延时机制)。

在 Go 语言中,我们可以使用 defer 关键字延迟执行一些代码,无论外部函数执行的怎样,这些代码都会在函数返回之前被执行。

下面是一个示例,展示了 defer 的使用场景:

import "fmt"func sum(n1 int, n2 int) int {defer fmt.Println("ok1 n1=", n1)defer fmt.Println("ok2 n2=", n2)res := n1 + n2fmt.Println("ok3 res=", res)return res
}func main() {res := sum(10, 20)fmt.Println("res=", res)
}

运行上面的代码,输出结果为:

ok3 res= 30
ok2 n2= 20
ok1 n1= 10
res= 30

当go执行到一个defer时,不会立即执行defer后的语句,而是将defer后的语句压入到一个栈中,当函数执行完毕之后,再从defer栈中,一次从栈顶取出语句执行,所以从上面的案例就可以看出,先执行的是ok2语句。

3.8 作为结构体的方法

在 Go 语言中,方法是一种与特定类型关联的函数。它们可以通过定义在类型上的方法来实现某些特定操作。

下面是一个示例,展示了如何定义和使用方法:

package mainimport ("fmt""math"
)// 定义一个名为Circle的结构体类型
type Circle struct {radius float64
}// 在Circle类型上定义一个名为area的方法,它返回这个圆的面积
func (c Circle) area() float64 {return math.Pi * c.radius * c.radius
}func main() {// 创建一个Circle实例c := Circle{radius: 5}// 调用Circle类型的方法fmt.Println("圆的面积:", c.area())
}

运行上面的代码,输出结果为:

圆的面积: 78.53981633974483

在这个示例中,我们定义了一个名为 Circle 的结构体类型,它包含一个半径属性。然后,在 Circle 类型上定义了一个名为 area 的方法,它用于计算圆的面积。

main 函数中,我们创建了一个 Circle 实例,并调用了 Circle 类型的 area 方法来计算面积。

通过方法,我们可以将某些操作与特定类型绑定,使得代码更加清晰和面向对象。

4 变量的作用域

  1. 函数内部声明/定义的遍历叫局部变量,作用域仅限于函数内部。

    func test() {// age 和 name 的作用域就只在test函数内部age := 10Name := "Tom~"
    }func main() {
    }
    
  2. 函数外部声明/定义的变量叫全局变量,作用域在整个包都有效,如果其首字母为大写,则作用域在整个程序有效。

    //函数外部声明/定义的变量叫全局变量,
    //作用域在整个包都有效,如果其首字母为大写,则作用域在整个程序有效
    var age int = 50
    var Name string = "jack~"//函数
    func test() {//age 和 Name的作用域就只在test函数内部age := 10Name := "tom~"fmt.Println("age=", age) // 10fmt.Println("Name=", Name) // tom~
    }func main() {fmt.Println("age=", age) //  50fmt.Println("Name=", Name) // jack~test()
    }
    
  3. 如果变量是在一个代码块,比如 for / if中,那么这个变量的作用域就在该代码块。

package main
import ("fmt"
)
func main() {//如果变量是在一个代码块,比如 for / if中,那么这个变量的的作用域就在该代码块for i := 0; i <= 10; i++ {fmt.Println("i=", i)}var i int //局部变量for i = 0; i <= 10; i++ {fmt.Println("i=", i)}fmt.Println("i=", i)
}

Over~~~~结束啦!!!!冲冲冲!!!

相关文章:

Go学习第五章——函数与包

Go学习第五章——函数与包 1 函数1.1 基本语法1.2 函数多返回值1.3 函数的可见性和包级函数1.4 函数调用机制底层原理1.5 值类型和引用类型1.6 注意事项和细节1.7 逃逸机制&#xff08;补&#xff0c;可不看&#xff09; 2 包2.1 快速入门2.2 包的使用细节 3 函数详细讲解3.1 递…...

【Python 常用脚本及命令系列 5 -- 如何使用 BeautifulSoup 解析CSDN网页表格中的数据】

文章目录 Python BeautifulSoup 介绍CSDN 网页表格解析开发问题总结 Python BeautifulSoup 介绍 BeautifulSoup是一个Python库&#xff0c;用于解析HTML和XML文档。它常常用于网络爬虫来提取网页中的信息。 以下是BeautifulSoup的一些主要特性&#xff1a; 解析HTML&#xff…...

OpenFeign实现分析、源码解析

什么是openfeign? 是springcloud全家桶的组件之一&#xff0c;其核心作用是为Rest API提供高效简洁的rpc调用方式。 为什么只定义接口而没有实现类&#xff1f; 源码解读&#xff08;省略&#xff09; 总结&#xff1a; 源码分析&#xff1a;如何发送http请求&#xff1f; …...

2023 10月最新Vmd 下载安装教程,WindowsLinux

文章目录 下载Vmdwindows版本安装LINUX版本安装 下载Vmd 谷歌搜索VMD 点击左下角download VMD 可选择对应版本 注&#xff1a;点击后会出现输入用户名和密码&#xff0c;由于我已注册&#xff0c;界面不见了&#xff0c;所以直接描述一下。 输入用户名和密码然后会出现让登记…...

Photoshop(PS)安装教程(图文教程超详细)

目录 一.简介 二.安装步骤 软件&#xff1a;PS版本&#xff1a;2023语言&#xff1a;简体中文大小&#xff1a;3.20G系统要求&#xff1a;Win10&#xff08;1903&#xff09;及以上版本&#xff0c;64位操作系统硬件要求&#xff1a;CPU2.0GHz 内存8G(或更高&#xff0c;不支…...

C++模版进阶

一、非类型模版参数 之前学习的模版&#xff0c;参数一般是某种类型&#xff0c;但其实非类型的参数也可以定义在模版里面&#xff0c;但也有一定的限制&#xff0c;只可以定义整形家族的参数&#xff0c;而且具有常量性 注意&#xff1a; 1. 浮点数、类对象以及字符串是不允…...

CloudCompare

CloudCompare 源码编译Windows 功能格式转换 源码编译 Windows 源码编译出来的默认基本不带几个插件&#xff0c;包括保存为 .las 的功能 可以直接从 https://www.danielgm.net/cc/ 下载编译好的版本&#xff0c;插件比较多。也有免安装版本 cmake -B build -S . -G "Vi…...

【算法小课堂】深入理解前缀和算法

前缀和是指某序列的前n项和&#xff0c;可以把它理解为数学上的数列的前n项和&#xff0c;而差分可以看成前缀和的逆运算。合理的使用前缀和与差分&#xff0c;可以将某些复杂的问题简单化。 我们通过一个例子来理解前缀和算法的优势&#xff1a; 一维前缀和&#xff1a; ww…...

元对象系统功能

元对象系统功能 建立工程 布局页面 布局页面 修改原件名称 建立元对象 函数作为接口 增加一些固定的属性 #------------------------------------------------- # # Project created by QtCreator 2023-10-24T21:54:44 # #----------------------------…...

【2024秋招】小米中间件后端开发一面2023-9-13-base武汉

1 自我介绍 2 快手实习 2.1 讲讲你写的curd启动器&#xff0c;做了哪些工作呢 答&#xff1a; 2.2 网上也有一些开源的curd代码生成器&#xff0c;你为什么需要自研呢&#xff08;重要&#xff09; 答&#xff1a; &#xff08;1&#xff09;这个必须得自研&#xff0c;因…...

SpringMVC Day 01:入门案例

前言 在我们的日常工作和学习中&#xff0c;Web 开发是一个无法回避的重要环节。而在 Java Web 开发领域&#xff0c;SpringMVC 无疑是一个重量级选手。它以其灵活性、强大功能和清晰的 MVC 结构&#xff0c;赢得了大量开发者的青睐。但是&#xff0c;对于初学者来说&#xff…...

docker、docker-compose安装教程,很详细

docker、docker-compose安装教程&#xff0c;很详细 一、卸载旧版1、查看有没有安装过旧版2、停止docker3、删除安装过docker的相关包4、删除docker相关的镜像和容器 二、docker安装1、设置阿里云镜像2、查看所有docker3、安装最新版本4、安装指定版本 三、使用前准备1、启动do…...

源代码转换:Tangible Software Solutions 23.10 Crack

Tangible Software Solutions The Most Accurate and Reliable Source Code Converters Convert between C#, Java, C, Python, & VB, while saving countless hours of painstaking work and valuable time.源代码转换 Key Benefits Saves valuable time Accurate and com…...

SAD notes

ESKF 总结 prediction 更新误差先验 F F F通过3.42来算 得到 这里有点绕的一点是: 误差状态的 F F F牵涉到名义状态, 而名义状态又需要在时间上推进更新 其中, F中的名义状态的推进通过公式3.41得到, (名义状态不考虑误差, 这一点从3.41d, 3.41e可以看出, 误差状态只考虑…...

[SQL开发笔记]BETWEEN操作符:选取介于两个值之间的数据范围内的值

一、功能描述&#xff1a; BETWEEN操作符&#xff1a;选取介于两个值之间的数据范围内的值。这些值可以是数值、文本或者日期。 二、BETWEEN操作符语法详解&#xff1a; BETWEEN操作符语法&#xff1a; SELECT column1, column2,…FROM table_nameWHERE column BETWEEN val…...

Babylonjs学习笔记(三)——创建天空盒

书接上回&#xff0c;这里讨论创建天空盒&#xff01;&#xff01;&#xff01; // 天空盒const envTex CubeTexture.CreateFromPrefilteredData(./env/environmentSpecular.env,scene)scene.environmentTexture envTex;scene.createDefaultSkybox(envTex,true)scene.environ…...

【计算机网络】文件传输协议FTP和SFTP

1. 介绍 SFTP&#xff08;SSH文件传输协议&#xff09;和FTP&#xff08;文件传输协议&#xff09;都是用于在计算机之间传输文件的网络协议。FTP和SFTP都位于OSI模型中的应用层。这两种协议用于文件传输和管理&#xff0c;是应用层协议&#xff0c;因此它们工作在OSI模型的最…...

Python 编程语言的介绍

Python 是一种高级、动态类型的解释型语言。由 Guido van Rossum 于1989年底发明&#xff0c;并在1991年首次发布。Python 的设计哲学强调代码的可读性和简洁的语法&#xff0c;特别是使用缩进来表示代码块&#xff0c;这使得开发者能够用更少的代码表达想法。 基础概念: 语法…...

centos服务器搭建安装Gitlab教程使用教程

1、更新服务器&#xff1a; sudo yum update -y && sudo yum upgrade -y 2、下载Gitlab的RPM包 https://packages.gitlab.com/gitlab/gitlab-cece表示开源el表示centos 选64位el8对应CentOS8 本教程以centos8为例&#xff0c;在服务器中&#xff0c;下载centos8的…...

linux复习笔记02(小滴课堂)

linux下输入输出错误重定向&#xff1a; 输入重定向&#xff1a;< 一个大于号是进行了覆盖。 两个大于号是追加。 输出重定向可以用于以后日志打印。 错误重定向&#xff1a; 错误重定向是不把信息打印到屏幕上而是打印到指定文件中去&#xff1a; 输出重定向其实是用的1…...

AWVS漏洞扫描使用基础与介绍

漏洞扫描的基本概念和原理 漏洞扫描是指通过使用自动化工具和技术来检测和识别计算机系统和网络中可能存在的安全漏洞&#xff0c;用于帮助网络安全运维人员及时获取网络安全态势。漏洞扫描是网络安全中的重要环节&#xff0c;它可以帮助我们发现和修复网络中的安全漏洞&#x…...

Flink 维表关联

1、实时查询维表 实时查询维表是指用户在 Flink 算子中直接访问外部数据库&#xff0c;比如用 MySQL 来进行关联&#xff0c;这种方式是同步方式&#xff0c;数据保证是最新的。但是&#xff0c;当我们的流计算数据过大&#xff0c;会对外 部系统带来巨大的访问压力&#xff0…...

阳光蟹场小程序的盈利模式与思考深度

随着移动互联网的快速发展&#xff0c;小程序成为了各行各业进行数字化转型的重要工具之一。阳光蟹场小程序作为一款专为蟹场管理和销售提供支持的移动&#xff0c;其盈利模式也备受关注。本文将从阳光蟹场小程序的盈利途径、商业模式和对蟹场管理的影响等方面&#xff0c;深入…...

2-Java进阶知识总结-7-UDP-TCP

文章目录 网络编程概述网络编程三要素--IP地址IP地址--概念&#xff08;IP&#xff1a;Internet Protocol&#xff09;IP地址--分类IP地址--特殊的地址&#xff1a;127.0.0.1IP地址获取--DOS命令IP地址获取--InetAddress类 网络编程三要素--端口端口--概念端口号 网络编程三要素…...

C++数据结构X篇_19_排序基本概念及冒泡排序(重点是核心代码,冒泡是稳定的排序)

文章目录 1. 排序基本概念2. 冒泡排序2.1 核心代码2.2 冒泡排序代码2.3 查看冒泡排序的时间消耗2.4 冒泡排序改进版减小时间消耗 1. 排序基本概念 现实生活中排序很重要&#xff0c;例如:淘宝按条件搜索的结果展示等。 概念 排序是计算机内经常进行的一种操作&#xff0c;其目…...

工作:三菱伺服驱动器连接参数及其电机钢性参数配置与调整

工作&#xff1a;三菱伺服驱动器参数及电机钢性参数配置与调整 一、三菱PLC与伺服驱动器连接参数的设置 1. 伺服配置 单个JET伺服从站链接侧占用点数:Rx/Ry占用64点、RWw/RWr占用32点 图中配置了22个JET伺服从站&#xff0c;占用点数:Rx/Ry占用64222048‬点、RWw/RWr占用322…...

企事业单位/公司电脑文件透明加密保护 | 防泄密软件\系统!

推荐——「天锐绿盾电脑文件防泄密系统」 一款全面的企业/公司数据透明加密防泄密系统&#xff0c;旨在从源头上保障数据的安全和使用安全。 PC访问地址&#xff1a; https://isite.baidu.com/site/wjz012xr/2eae091d-1b97-4276-90bc-6757c5dfedee 它具有以下特点&#xff1a…...

[Leetcode] 0101. 对称二叉树

101. 对称二叉树 题目描述 给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 示例 1&#xff1a; 输入&#xff1a;root [1,2,2,3,4,4,3] 输出&#xff1a;true示例 2&#xff1a; 输入&#xff1a;root [1,2,2,null,3,null,3] 输出&#xff1a;false提示&#…...

.NET、VUE利用RSA加密完成登录并且发放JWT令牌设置权限访问

后端生成公钥私钥 使用RSA.ToXmlString(Boolean) 方法生成公钥以及私钥。 RSACryptoServiceProvider rSA new(); string pubKey rSA.ToXmlString(false);//公钥 string priKey rSA.ToXmlString(true);//私钥 后端将生成的公钥发送给前端 创建一个get请求&#xff0c;将…...

go实现文件的读写

读文件 1.ioutil.ReadFile package mainimport ("fmt""io/ioutil" )func main() {filePath : "example.txt"data, err : ioutil.ReadFile(filePath)if err ! nil {fmt.Printf("无法读取文件&#xff1a;%v\n", err)return}fmt.Print…...