【启程Golang之旅】深入解析函数的奥秘与技巧

欢迎来到Golang的世界!在当今快节奏的软件开发领域,选择一种高效、简洁的编程语言至关重要。而在这方面,Golang(又称Go)无疑是一个备受瞩目的选择。在本文中,带领您探索Golang的世界,一步步地了解这门语言的基础知识和实用技巧。

目录

初识函数

引入包

init函数

匿名函数

闭包

defer关键字

系统函数

错误处理


初识函数

为完成某一功能的程序指令(语句)的集合称为函数,函数的作用就是提高代码的复用性和可维护性,减少代码的冗余。接下来我们举一个简单的例子,看一看函数如何进行处理和调用:

package main
import "fmt"
/*	函数基本格式
	func 函数名(参数列表) (返回值列表) {
		函数体
		return 返回值列表
	}
*/
// 求和函数
func cal(a, b int) int {
	return a + b
}
func main() {
	sum := cal(1, 2)
	fmt.Println(sum) // 3
}

函数有一些区别于其他语言的一些细节所在,接下来本文将对这些细节及其所起作用一一讲解:

函数名

1)遵循标识符命名规范:见名知意addNum,驼峰命名addNum

2)首字母不能是数字

3)首字母大写该函数可以被本包文件和其它包文件使用(类似public)

4)首学母小写只能被本包文件使用,其它包文件不能使用(类似private)

形参列表

1)形参列表:个数:可以是一个参数,可以是n个参数,可以是0个参数

2)形式参数列表:作用:接收外来的数据

3)实际参数:实际传入的数据

返回值类型列表

可以是多个,根据具体进行进行返回。

package main
import "fmt"
// 求和函数
func cal(a, b int) (int, int) {
	result1 := a + b
	result2 := a - b
	return result1, result2
}
func main() {
	sum1, sum2 := cal(1, 2)
	// 如果不想接收第二个值,可以使用下划线占位进行忽略
	//sum1, _ := cal(1, 2)
	fmt.Println(sum1, sum2) // 3  -1
}

函数不支持重载

在C++、Java(从C++11开始支持方法重载)等语言中。它允许程序员在相同的作用域内定义多个具有相同名称但参数列表(参数类型、参数个数或参数顺序)不同的函数。

// Go语言中不支持函数重载,但可以使用不同的函数名  
func printInt(x int) {  
    fmt.Println("Printing an integer:", x)  
}  
func printFloat(x float64) {  
    fmt.Println("Printing a float:", x)  
}  

func main() {  
    printInt(10)      // 调用printInt  
    printFloat(10.5)  // 调用printFloat  
} 

支持可变参数

golang中支持可变参数(如果你希望函数带有可变数量的参数)

package main
import "fmt"
// 定义一个函数,函数参数为:可变参数 ... 参数的数量可变
// args...int 可以传入任意多个数量的int类型的数据,传入0个,1个...n个
func test(args ...int) {
	// 函数内部处理可变参数的时候,可将可变参数当作切片来处理
	// 遍历可变参数
	for i := 1; i < len(args); i++ {
		fmt.Println(i, args[i])
	}
}

func main() {
	test(1, 2) // 1 2
	fmt.Println("-----------")
	test() // 空
}

值拷贝

基本数据类型和数组默认都是值传递的即进行值拷贝。在函数内修改不会影响到原来的值。

package main
import "fmt"
func test(num int) {
	num = 30
	fmt.Println(num)
}

func main() {
	var num int = 10
	test(num) // 30
	fmt.Println(num) // 10
}

引用传递

以值传递的方式的数据类型,如果希望在函数内的变量能够修改函数外的变量,可以传入变量的地址&,函数内以指针的方式操作变量,从效果来看类似引用传递。

package main
import "fmt"
func test(num *int) {
	*num = 30
	fmt.Println(num)
}

func main() {
	var num int = 10
	fmt.Println(&num) // 0xc00000a0c8
	test(&num)        // 调用test函数,将num的地址作为参数传递给test函数,test函数中修改num的值,结果为0xc00000a0c8
	fmt.Println(num)  // 30
}

赋值变量

在Go中,函数世是一种数据类型,可以赋值给一个变量,则该变量就是一个函数类型的变量了。通过该变量可以对函数调用。.

package main
import "fmt"
// 定义函数
func test(num int) {
	fmt.Println(num)
}

func main() {
	// 函数也是一种数据类型,可以赋值给变量
	a := test
	fmt.Printf("a的类型是: %T, test函数的类型是:%T \n", a, test) // a的类型是: func(int), test函数的类型是:func(int)
	// 通过赋值变量,调用函数
	a(10) // 10
}

形参调用

函数既然是一种数据类型,因此在Go中,函数可以作为形参,并且调用(把函数本身当做一种数据类型)

package main
import "fmt"
// 定义函数
func test(num int) {
	fmt.Println(num)
}
// 定义函数,把函数作为参数传递给另一个函数
func test2(num int, testfunc func(int)) {
	fmt.Println("---")
	testfunc(num)
}

func main() {
	// 函数也是一种数据类型,可以赋值给变量
	a := test
	fmt.Printf("a的类型是: %T, test函数的类型是:%T \n", a, test) // a的类型是: func(int), test函数的类型是:func(int)
	// 通过赋值变量,调用函数
	a(10) // 10
	// 函数作为参数传递给另一个函数
	test2(10, test) // --- 10
}

自定义数据类型

为了简化数据类型定义,Go支持自定义数据类型基本语法,type自定义数据类型名数据类型

可以理解为:相当于起了一个别名例如:typemylnt int -----》这时mylnt就等价int来使用了。

// 自定义数据类型(相当于起别名):给int类型起了别名叫myInt类型
type myInt int
var num1 myInt = 10
fmt.Println(num1) // 10

//var num2 int = 10
//num2 = num1 // 虽然是别名,但是在go语言中,别名是不能进行隐式转换的,还是认为myInt与int类型不同
package main
import "fmt"
// 定义函数
func test(num int) {
	fmt.Println(num)
}
// 定义函数,把函数作为参数传递给另一个函数
// 自定义数据类型
type myfunc func(int)
func test2(num int, testfunc myfunc) {
	fmt.Println("---")
	testfunc(num)
}

func main() {
	// 函数也是一种数据类型,可以赋值给变量
	a := test
	fmt.Printf("a的类型是: %T, test函数的类型是:%T \n", a, test) // a的类型是: func(int), test函数的类型是:func(int)
	// 通过赋值变量,调用函数
	a(10) // 10
	// 函数作为参数传递给另一个函数
	test2(10, test) // --- 10
}

当然这里还支持函数返回值命名,里面顺序无所谓,具体如下代码示例:

package main
import "fmt"
func test(num1 int, num2 int) (sum int, sub int) {
	sum = num1 + num2
	sub = num1 - num2
	return
}

func main() {
	sum, sub := test(10, 2)
	fmt.Println(sum, sub) // 12 8
}

引入包

在Go语言中,包(package)是一个非常重要的概念,它用于组织和管理代码。

包的概念

1)组织代码:包是Go语言中代码的基本组织单元。它将相关的函数、类型、变量和常量等组合在一起,形成一个逻辑上的整体。

2)命名空间:每个包都有其自己的命名空间,包内的标识符(如函数名、类型名、变量名等)在该包内是唯一的,但在不同的包中可以重名。通过包名可以区分和引用不同包中的标识符。

3)封装性:包可以隐藏其内部实现细节,只对外暴露必要的接口。这有助于保护代码的封装性,减少不同模块之间的耦合度。

使用意义

1)代码复用:通过将代码组织成包,可以方便地在不同的项目或模块中复用这些代码。只需要将包导入(import)到需要使用的地方,就可以使用其中的函数、类型和变量等。

2)模块化管理:Go语言的包机制支持模块化管理,可以将大型项目拆分成多个小模块(即包),每个模块负责实现特定的功能。这样可以提高代码的可读性和可维护性,降低项目的复杂度。

3)类型安全:由于每个包都有自己的命名空间,因此可以避免不同包中的标识符重名导致的类型冲突问题。这有助于提高代码的类型安全性。

4)隐藏实现细节:通过将实现细节封装在包内部,只对外暴露必要的接口,可以隐藏实现细节,降低外部对内部实现的依赖和耦合度。这有助于提高代码的可维护性和可扩展性。

5)遵循最佳实践:Go语言的包机制鼓励开发者遵循最佳实践,如将相关的函数和类型组织在同一个包中,使用统一的命名规范等。这有助于提高代码的可读性和可维护性。

以下是导包后调用的基础案例:

我们在导包的时候,也是需要关注一些细节方面的内容,方便我们快速导包使用,细节如下:

1)package进行包的声明,建议:包的声明这个包和所在的文件夹同名

2)main包是程序的入口包,一般main函数会放在这个包下

3)打包语法:package 包名

4)引l入包的语法:import"包的路径”包名是从$GOPATH/src/后开始计算的,使用/进行路径分隔。

5)如果有多个包,建议一次性导入,格式如下:

        import(

                "fmt"

                "dbutils"

        )

6)在函数调用的时候前面要定位到所在的包

7)首字母大写,函数可以被其它包访问

8)一个目录下不能有重复的函数

9)包名和文件夹的名字,可以不一样

10)一个目录下的同级文件归属一个包

11)在程序层面,所有使用相同package包名的源文件组成的代码模块,在源文件层面就是一个文件夹

当然也可以给包起别名,起完别名之后原来的包名就不能再使用了:

init函数

初始化函数,可以用来进行一些初始化的操作每一个源文件都可以包含一个init函数,该函数会在main函数执行前,被Go运行框架调用。示例代码如下所示:

全局遍历、init函数、main函数的执行顺序流程如下:

多个init函数情况,先执行外包中的init函数,最后执行main函数中的init函数,示例如下:

匿名函数

go语言支持匿名函数,如果我们某个函数只是希望使用一次,可以考虑使用匿名函数,以下是使用匿名函数的使用方式:

定义匿名函数时就直接调用,这种方式匿名函数只能调用一次:

package main
import "fmt"
func main() {
	// 定义匿名函数
	result := func(num1 int, num2 int) int {
		return num1 + num2
	}(10, 3)
	fmt.Println(result) // 13
}

将匿名函数赋给一个变量(该变量就是函数变量了),再通过该变量来调用匿名函数:

package main
import "fmt"
func main() {
	// 将匿名函数赋值给变量,然后通过变量调用匿名函数
	sub := func(a, b int) int {
		return a - b
	}
	// 调用匿名函数
	result := sub(10, 5)
	fmt.Println(result) // 5
}

将匿名函数作为全局变量使用:

package main
import "fmt"
// 设置全局匿名函数
var sub = func(a, b int) int {
	return a - b
}

func main() {
	// 调用匿名函数
	result := sub(10, 5)
	fmt.Println(result) // 5
}

闭包

闭包就是一个函数和与其相关的引|用环境组合的一个整体。

闭包的本质:是一个匿名函数,只是这个函数引入外界的变量 / 参数匿名函数 + 引用的变量 / 参数 = 闭包,示例代码如下:

package main
import "fmt"
// 函数求和,函数的名字是getSum,参数为空
// getSum返回一个函数,这个函数是int类型的参数,返回值也是int类型
func getSum() func(int) int {
	var sum int = 0
	return func(num int) int {
		sum += num
		return sum
	}
}

func main() {
	f := getSum()
    // 可以看到匿名函数中引用的那边变量会一直保存再内存中,可以一直使用
	fmt.Println(f(1)) // 1
	fmt.Println(f(2)) // 3
	fmt.Println(f(3)) // 6
}

闭包的特点:返回的是一个匿名函数,但是这个匿名函数引用到函数外的变量/参数,因此这个匿名函数就和变量/参数形成一个整体,构成闭包。包中使用的变量/参数会一直保存在内存中,所以会一直使用,这就意味着闭包不可滥用。

defer关键字

在函数中,程序员经常需要创建资源,为了在函数执行完毕后,及时的释放资源,Go的设计者提供defer关键字,接下来通过如下代码进行讲解:

遇到defer关键字会将后面的代码语句压入栈中,也会将相关的值同时拷贝入栈中,不会随着函数后面的变化而变化。代码如下:

package main
import "fmt"
func add(num1, num2 int) int {
	// 在go中,程序遇到defer关键字,不会立即执行defer后面的语句,而是将defer后面的语句放入到栈中,然后执行函数后面的语句。
	defer fmt.Println("num1 = ", num1)
	defer fmt.Println("num2 = ", num2)
	// 栈的特点是先进后出,所以defer后面的语句会先执行
	// 在函数执行完毕后,从栈中取出语句开始执行,按照先进后出的顺序执行
	sum := num1 + num2
	fmt.Println("sum = ", sum)
	return sum
}
func main() {
	fmt.Println(add(1, 2))
}

最终得到的结果如下所示:

defer应用场景:比如你想关闭某个使用的资源,在使用的时候直接随手defer,因为defer有延迟执行机制(函数执行完毕再执行defer压入栈的语句),所以你用完随手写了关闭,比较省心,省事!

系统函数

系统函数是一个相对模糊的概念,它可以指代任何与底层系统交互或执行特定系统级别任务的函数。在Go语言中,这些函数可能来自标准库、第三方库、通过系统调用实现的功能或者内置的函数,接下来我们就讲解一下go语言中常见的内置函数,使用内置函数也不用导包的,直接用就行:

len(str):统计字符串的长度,按字节进行统计

func main() {
	// 统计字符串的长度,按字节进行统计
	str := "hello world"
	fmt.Println(len(str)) // 11
}

r:=[]rune(str):字符串遍历

func main() {
	str := "hello world"
	// 对字符串进行变量
	// 方式1:利用键值循环:for - range
	for i, value := range str {
		fmt.Printf("索引为:%d, 具体的值为:%c \n", i, value)
	}
	fmt.Println("---------------------------------")
	// 方式2:利用 r:=[]rune(str)
	r := []rune(str)
	for i := 0; i < len(r); i++ {
		fmt.Printf("索引为:%d, 具体的值为:%c \n", i, r[i])
	}
}

最终得到的结果如下所示:

n, err := strconv.Atoi("66"):字符串转整数;str = strconv.ltoa(6887):整数转字符串

func main() {
	// 字符串转整数
	num1, _ := strconv.Atoi("123")
	fmt.Println(num1) // 123
	// 整数转字符串
	num2 := strconv.Itoa(123)
	fmt.Println(num2) // "123"
}

strings.Contains("javaandgolang", "go"):查找子串是否在指定的字符串中

func main() {
	// 查找子串是否在指定的字符串中
	result := strings.Contains("javaandgolang", "go")
	fmt.Println(result) // true
}

strings.Count("javaandgolang","a"):统计一个字符串有几个指定的子串

func main() {
	// 查找子串是否在指定的字符串中
	result := strings.Count("javaandgolang", "a")
	fmt.Println(result) // 4
}

fmt.Println(strings.EqualFold("go", "Go")):不区分大小写的字符串比较

func main() {
	fmt.Println(strings.EqualFold("go", "Go")) // true
}

strings.Index("javaandgolang", "a"):返回子串在字符串第一次出现的索引值,如果没有返回-1

func main() {
	result := strings.Index("javaandgolang", "a")
	fmt.Println(result) // 1
}

当然下面还有一些函数,这里就不再一一演示了:

strings.Replace("goandjavagogo", "go","golang", n):字符串的替换,n表示替换几个

strings.Split("go-python-java", "-"):按照指定的某个字符,为分割标识,将一个学符串拆分成字符串数组

strings.ToLower("Go") // go;strings.ToUpper"go") //Go:字符串的字母进行大小写的转换

strings.TrimSpace("   go and java    "):将字符串左右两边的空格去掉

strings.Trim("~golang~ ", " ~"):将字符串左右两边指定的字符去掉

strings.TrimLeft("~golang~ ", " ~")strings.TrimRight("~golang~ ", " ~"):指定字符两边去掉

strings.HasPrefix("http://java.sun.com/jsp/jstl/fmt", "http"):判断字符串是否以指定的字符串开头

当然go语言还内置了时间函数,这里进行一个简单的演示:

package main
import (
	"fmt"
	"time"
)
func main() {
	// 日期时间函数
	now := time.Now()
	// Now() 是一个结构体,类型是time.Time, 返回的是当前时间
	// 2024-05-26 16:05:18.4238005 +0800 CST m=+0.000660501 ~~~ 对应的类型为: time.Time
	fmt.Printf("%v ~~~ 对应的类型为: %T\n", now, now)

	// 获取年月日
	fmt.Printf("年: %d\n", now.Year())  // 2024
	fmt.Printf("月: %d\n", now.Month()) // 5
	fmt.Printf("日: %d\n", now.Day())   // 26

	// 获取当前年月日时分秒的字符串 Format()函数是结构体的方法,作用是格式化时间
	fmt.Printf("年月日时分秒: %s\n", now.Format("2006-01-02 15:04:05")) // 2024-05-26 16:05:18
}

最终达到的效果如下所示:

错误处理

当函数出现运行错误的时候,会导致程序被中断,是无法继续执行后续代码的,所以我们需要对错误进行一个处理,以下是程序出现错误时的情况:

在Go语言中,deferrecover是两个与函数延迟执行和异常处理相关的关键字。它们主要用于错误处理和资源清理等场景。

package main
import "fmt"
func test() {
	// 利用defer+recover机制,捕获异常,defer后加上匿名函数的调用
	defer func() {
		// 调用recover()函数,捕获异常
		err := recover()
		if err != nil {
			// 捕获异常后,执行相应的处理逻辑
			fmt.Println("错误已经捕获")
			fmt.Println("捕获异常:", err)
		}
	}()
	num1 := 10
	num2 := 0
	result := num1 / num2
	fmt.Println(result)
}

func main() {
	test()
	fmt.Println("上述逻辑正常,开始执行下面的代码~")
	fmt.Println("main函数执行完毕")
}

最终实现的效果如下所示:

如果想自定义错误的话,可以采用下面的方式进行:

package main
import (
	"errors"
	"fmt"
)
func test() (err error) {
	num1 := 10
	num2 := 0
	// 自定义错误
	if num2 == 0 {
		// 抛出自定义错误
		return errors.New("除数不能为0")
	} else {
		result := num1 / num2
		fmt.Println(result)
		// 如果没有错误,返回零值
		return nil
	}
}

func main() {
	err := test()
	if err != nil {
		fmt.Println("自定义错误", err)
	}
	fmt.Println("上述逻辑正常,开始执行下面的代码~")
	fmt.Println("main函数执行完毕")
}

最终实现的结果如下所示:

有一种情况:程序出现错误以后,后续代码就没有必要执行,想让程序中断,退出程序:借助:builtin包下内置函数:panic

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

html5+css3+js学习记录(1)-- html

1 vscode前端插件 1.1 Web标准 2 文档声明与字符编码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewpor…

【全开源】海报在线制作系统源码(ThinkPHP+FastAdmin+UniApp)

打造个性化创意海报的利器 引言 在数字化时代&#xff0c;海报作为一种重要的宣传媒介&#xff0c;其设计质量和效率直接影响着宣传效果。为了满足广大用户对于个性化、高效制作海报的需求&#xff0c;海报在线制作系统源码应运而生。本文将详细介绍海报在线制作系统源码的特…

【电子元件】TL431 电压基准

TL431(C23892)是一种常用的可调节精密电压基准和电压调节器。它广泛应用于电源管理、精密参考电压和稳压电路等领域。以下是TL431的一些关键特点和使用方法&#xff1a; 关键特点 可调输出电压&#xff1a;TL431的输出电压可以通过外部电阻网络在2.495V到36V范围内调整。精度高…

【知识图谱】探索攻略:基础、构建、高级应用与相关论文方向

【知识图谱】相关文章汇总 写在最前面一、什么是知识图谱&#xff1f;二、相关历史文章代码实现&#xff1a;简单的知识图谱可视化知识图谱前身&#xff1a;信息抽取知识图谱应用1&#xff1a;社交网络分析知识图谱应用2&#xff1a;威胁情报挖掘知识图谱应用3&#xff1a;Code…

当AWR1843发送完设置的固定帧后,如何使其再发送第一次的帧?

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…

人类最友好语言? YAML 深入解析:从语法到最佳实践

什么是YAML YAML&#xff08;YAML Ain’t Markup Language&#xff09;是一种人类可读的数据序列化语言。它的设计目标是使数据在不同编程语言之间交换和共享变得简单。YAML采用了一种简洁、直观的语法&#xff0c;以易于阅读和编写的方式表示数据结构。 YAML广泛应用于配置文…

Elasticsearch的Index sorting 索引预排序会导致索引数据的移动吗?

索引预排序可以确保索引数据按照指定字段的指定顺序进行存储&#xff0c;这样在查询的时候&#xff0c;如果固定使用这个字段进行排序就可以加快查询效率。 我们知道数据写入的过程中&#xff0c;如果需要确保数据有序&#xff0c;可能需要在原数据的基础上插入新的数据&#…

Youngter-drive

BUUCTF逆向题Youngter-drive-CSDN博客 逆向每日一题----Youngter-drive题解-CSDN博客 借鉴博客,写得比我好 upx拖壳 upx -d Youngter-drive.exe 这道题我不知道为什么,我这里是运行不了的,也没有找到原因 int __cdecl main_0(int argc, const char **argv, const char **env…

进程信号(1)

目录 一、信号 1.1、生活中的信号 1.2、Linux中的信号 二、信号处理常见方式 三、信号的产生 3.1、简单理解信号的保存和发送 3.2、键盘产生信号 3.3、核心转储 3.4、系统调用接口产生信号 3.4.1、kill 3.4.2、raise 3.4.3、abort 3.5、软件条件产生信号 3.6、硬…

网络编程-TCP

一、TCP的相关IP 1.1 SeverSocket 这是Socket类,对应到网卡,但是这个类只能给服务器使用. 1.2 Socket 对应到网卡,既可以给服务器使用,又可以给客户端使用. TCP是面向字节流的,传输的基本单位是字节. TCP是有连接的,和打电话一样,需要客户端拨号,服务器来听. 服务器的内核…

【Linux】为 VMware 的 Linux 系统(CentOS 7)设置静态IP地址

文章目录 准备工作查看 子网掩码 和 网关IP确认准备设置的虚拟机端口没有被占用 调整设置编辑配置文件配置文件说明 完成配置&#xff0c;准备测试使用命令终端连接服务器 我是一名立志把细节说清楚的博主&#xff0c;欢迎【关注】&#x1f389; ~ 原创不易&#xff0c; 如果有…

redis--消息队列

分类 生产者消费模式 发布者订阅模式 生产者消费模式 在生产者消费者(Producer/Consumer)模式下&#xff0c;上层应用接收到的外部请求后开始处理其当前步骤的操作&#xff0c;在执行完成后将已经完成的操作发送至指定的频道(channel)当中&#xff0c;并由其下层的应用监听…

开发公众号自定义菜单之创建菜单

文章目录 申请测试账号换取Token接口测试提交自定义菜单查看效果校验菜单配置清空菜单配置结束语 申请测试账号 https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?actionshowinfo&tsandbox/index 或 得到appid和secret 换取Token 使用appid和secret换取token令牌…

嵌入式实时操作系统笔记1:RTOS入门_理解简单的OS系统

今日开始学习嵌入式实时操作系统RTOS&#xff1a;UCOS-III实时操作系统 本次目标是入门RTOS&#xff0c;理解多任务系统...... 本文只是个人学习笔记&#xff0c;基本都是对网上资料的整合...... 目录 STM32裸机与RTOS区别&#xff1a; 裸机中断示例&#xff1a; RTOS对优先级…

飞鸡:从小训练飞行的鸡能飞行吗?为什么野鸡能飞吗?是同一品种吗?今天自由思考

鸡的飞行能力在很大程度上受到其生理结构的限制。尽管鸡有翅膀&#xff0c;但与能够长时间飞行的鸟类相比&#xff0c;鸡的翅膀相对较小&#xff0c;且胸部肌肉较弱。再加上鸡的身体较重&#xff0c;这些因素共同限制了鸡的飞行能力。通常&#xff0c;鸡只能进行短暂的、低空的…

Redis简介与安装到python的调用

前言 本文只不对redis的具体用法做详细描述&#xff0c;做简单的介绍&#xff0c;安装&#xff0c;和python代码调用详细使用教程可查看一下网站 https://www.runoob.com/redis/redis-tutorial.html https://pypi.org/project/redis/ 官方原版: https://redis.io/ 中文官网:…

【698协议】帧校验算法

698协议&#xff0c;帧校验算法 帧格式 帧校验范围 校验算法 #include "fcs16.h" /* * u16 represents an unsigned 16-bit number. Adjust the typedef for * your hardware. * Drew D. Perkins at Carnegie Mellon University. * Code liberally borrowed from M…

GB报文中的Cseq值的注意点

一、 问题现象 【问题现象】NVR使用GB接三方平台发现倍速回放时&#xff0c; 【现场拓扑】现场拓扑如下 &#xff08;1&#xff09; NVR侧使用家用宽带的方式&#xff0c;通过国标跨公网接入三方平台。 图1.1&#xff1a;网络拓扑 二、 抓包分析 INVITE sip:420000004013200…

异步获取线程执行结果,JDK中的Future、Netty中的Future和Promise对比

JDK中的Future和Netty中的Future、Promise的关系 三者源头追溯 Netty中的Future与JDK中的Future同名&#xff0c;但是是两个不同的接口。Netty中的Future继承自JDK的Future&#xff0c;而Promise又对Netty中的Future进行了扩展。 JDK中的Future源自JUC并发包&#xff1a; Net…

【LeetCode刷题】滑动窗口思想解决问题:长度最小的子数组、无重复字符的最长子串

【LeetCode刷题】Day 7 题目1&#xff1a;209.长度最小的子数组思路分析&#xff1a;思路1&#xff1a;暴力枚举 O(N^2^)思路2&#xff1a;滑动窗口 O(N) 题目2&#xff1a;3. 无重复字符的最长子串题目分析&#xff1a;思想1&#xff1a;暴力枚举哈希表O(N^2^)思想2&#xff1…