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

golang slice总结

目录

概述

一、什么是slice

二、slice的声明

三、slice的初始化、创建

make方式创建

创建一个包含指定长度的切片

创建一个指定长度和容量的切片

创建一个空切片

创建一个长度和容量都为 0 的切片

new方式创建

短声明初始化切片

通过一个数组来创建切片

声明一个 nil 切片

四、nil切片和空切片的区别

空切片(Empty Slice)

nil 切片(nil Slice)

共同点

注意事项和易错点

五、常见导致panic的slice操作

索引越界

切片越界

小结

六、slice的相关操作

slice底层数组指针的获取方式

方式1、unsafe.SliceData

方式2、%p

方式3、reflect

slice的长度

slice的容量

slice的遍历

基于索引的遍历

使用range实现遍历

slice的截取

slice的追加

内置append函数

append不扩容的情况

append扩容的情况

append的返回值很重要

slice的拷贝

浅拷贝

深拷贝

内置copy函数

内置copy函数实现深拷贝

内置append函数实现深拷贝

七、slice在函数调用时是header传递

总结


概述

        本文主要是对slice的总结,从slice的基本概念、创建方式、常用操作,自己经历过的坑点易错点,以及append的机制,slice的header传递,尽可能用最简单的代码说明问题,本文的代码是基于golang 1.22.1版本验证。

一、什么是slice

        切片是用来描述底层数组的连续片段的数据结构,本身并不保存数组数据,而只是保存数组连续片段的描述信息,通过结构体里的array段指针引用底层数组,长度和容量属性限制底层数组的读写片段。

type slice struct { array unsafe.Pointer len int cap int 
}

描述切片的数据结构由指向数组的指针、长度和容量三部分组成:

  • array 指针指向通过slice访问底层数组的的的第一个元素的指针,这里的被指位置不一定是数组的第一个元素,在对slice进行切片操作时,会移动这个指针。
  • len 长度代表切片当前包含的元素数量,len的大小不能超过cap的大小。
  • cap 容量表示底层数组从切片开始位置到数组末尾的元素个数,即当前切片的容量,cap总是大于或者等于len。

为什么指针的类型是 unsafe.Pointer 呢?

        这是因为在底层实现中,Go 语言的切片并不直接指向底层数组的数据,而是通过指针间接引用。unsafe.Pointer 类型是 Go 语言中用于处理底层指针的一种特殊类型,它可以指向任意类型的数据,包括未导出的结构体和数组。这种设计的目的是为了支持切片的动态扩展和收缩。当切片需要扩容时,底层的数组可能会被重新分配内存,新的数组可能位于内存的不同位置。使用指针可以更灵活地引用底层数组,而不受具体类型的限制。需要注意的是,由于 unsafe之所以叫unsafe包,是因为这里面的大部分操作绕开里golang的data type system的约束,直接操作内存单元,是比较危险,容易引起程序运行安全的操作,unsafe.Pointer 类型可以指向任意类型的数据,因此在使用时需要特别小心,避免造成内存访问越界或者类型不匹配的问题。通常情况下,开发者应该尽量避免直接使用 unsafe.Pointer,除非是在必要的情况下进行底层操作。

二、slice的声明

var slicename []T //slicevar slicename []T //slice

其中,slicename是切片的变量名,T 是切片中元素的类型 ,注意中括号里并没有数值n。

区分数组的声明

var arrayname [n]T //array

三、slice的初始化、创建

make方式创建

golang提供了内置的函数make创建

func make([]T, len, cap) []T
  • T 表示切片中元素的类型。
  • len 表示切片的长度,即切片中包含的元素个数。
  • cap 表示切片的容量,即底层数组的长度,这个参数可选

make函数调用后,它其实分配了一个T类型的数组, 并返回一个slice指向该数组

创建一个包含指定长度的切片

        当cap参数未指定时cap的值与len的值相同

s := make([]int, 5) // 创建一个包含 5 个整数的切片,初始值为对应类型的零值 
//[0 0 0 0 0] cap(s)= 5 len(s)= 5

创建一个指定长度和容量的切片

s := make([]int, 5, 10) // 创建一个包含 5 个整数的切片,并且底层数组的长度为 10 
//[0 0 0 0 0] cap(s)= 10 len(s)= 5

创建一个空切片

s :=make([]int, 0) // 创建一个空切片,长度为 0
//[] cap(s)= 0 len(s)= 0

创建一个长度和容量都为 0 的切片

s := make([]int, 0, 0) // 创建一个长度和容量都为 0 的切片 
//[] cap(s)= 0 len(s)= 0

new方式创建

         golang的内置函数new是用来分配内存,并返回指向该类型的零值的指针,而slice本身是一个包含指针、长度和容量的复合类型,使用new来创建slice不是很合适,这种方式还会涉及到unsafe包的使用,它绕过了Go的类型安全,并且需要手动管理内存,这很容易引发错误和内存泄漏。因此,在实际开发中,最好避免使用这种方法,而是使用make函数来创建slice,对于new不展开介绍

短声明初始化切片

s := []int{1, 2, 3, 4, 5} //[1 2 3 4 5] cap(s)= 5 len(s)= 5

通过一个数组来创建切片

arr := [5]int{1, 2, 3, 4, 5}
s := arr[:]
//[1 2 3 4 5]     cap(s)= 5  len(s)= 5s1 := arr[:]
s2 := arr[0:]
s3 := arr[:5]
// 以上 s s1 s2 s3都是相等的 都是[1 2 3 4 5]s4 := arr[:0]
//而s4是创建一个空的slice

声明一个 nil 切片

var s []int // 这是一个 nil 切片 //[] cap(s)= 0 len(s)= 0

四、nil切片和空切片的区别

        在 Go 中,空切片(empty slice)和 nil 切片(nil slice)是两种不同的概念,

它们有着共同点和某些不同的含义和用途:

空切片(Empty Slice)

  • 空切片是一个长度为 0 的切片,但其底层数组已经被分配了。
  • 可以通过 make 函数创建一个空切片,也可以通过切片字面量 []T{} 创建。
  • 空切片可以被使用,可以进行追加元素、遍历等操作,但不会引发 panic,因为底层数组已经被分配。
  • 通常用于表示一个空的集合或者没有元素的情况。
  • encoding/json编码时空切片会被编码为 JSON 数组 [],表示一个空的数组。当你有一个空的切片时,你期望它在 JSON 中被表示为一个空数组,这与空的集合或序列的语义一致。

示例:

// 使用 make 函数创建空切片
emptySlice := make([]int, 0)// 使用切片字面量创建空切片
emptySlice2 := []int{}fmt.Println("emptySlice ", emptySlice, " address of underlying array:", unsafe.SliceData(emptySlice), "\nlen=", len(emptySlice), "\ncap=", cap(emptySlice))
fmt.Println("emptySlice2 ", emptySlice2, " address of underlying array:", unsafe.SliceData(emptySlice2), "\nlen=", len(emptySlice2), "\ncap=", cap(emptySlice2))

 运行结果:

nil 切片(nil Slice)

  • nil 切片是一个指向 nil 的切片引用,即没有指向任何底层数组。
  • 一个 nil 切片的长度和容量都是 0,并且它的指针为 nil。
  • 可以将一个切片赋值为 nil,或者声明一个没有初始化的切片,它们都会被视为 nil 切片。
  • encoding/json编码时nil 切片会被编码为 JSON 的 null 值,表示不存在的值。当你有一个 nil 切片时,JSON 编码将把它解释为一个缺失值,这在某些情况下可能会引起问题,特别是在期望一个数组而不是 null 值的情况下

示例:

// 声明一个未初始化的切片,会被初始化为 nil 切片
var nilSlice []int
fmt.Println("nilSlice ", nilSlice, " address of underlying array:", unsafe.SliceData(nilSlice), "\nlen=", len(nilSlice), "\ncap=", cap(nilSlice))

运行结果:

共同点

        nil切片和空切片在进行如下操作:len(), cap(), append(), and for .. range loop时的行为和结果都是一致,都不会引发panic,都表示空的集合或序列(有的地方说对nil进行这些操作会引发panic,应该是跟go的版本不同)但是对nil slice的索引还是会抛出panic的,多一嘴:对nil map进行append操作会引发panic

注意事项和易错点

  • 使用空切片和 nil 切片的场景不同,需要根据实际情况进行选择。如果需要表示一个空的集合或者没有元素的情况,应该使用空切片;如果需要表示一个未初始化或者未指定的切片,可以使用 nil 切片。
  • 在函数返回值中,通常使用 nil 切片来表示某些特定条件下的空值。
  • 当需要传递一个切片作为函数参数,并且可能为空时,建议使用 nil 切片而不是空切片,以便明确表明该切片是未初始化的。
  • 在判断切片是否为空时,应该使用 len(slice) == 0 来判断,而不是 slice == nil,因为后者只能用于判断切片是否为 nil。
  • 对于 JSON 编码,需要注意的是:
  • 当你希望表示一个空的切片时,使用空切片 [];
  • 当你希望表示一个不存在的切片时,使用nil切片nil。

五、常见导致panic的slice操作

索引越界

        当访问slice的元素时,如果使用的索引超出了slice的有效范围(即小于0或大于等于slice的长度),程序会触发运行时panic,常见的是因为忽略slice或者array的起始索引是0,最后一个是len(slice)-1导致的。

这种panic提示一般是这样的:

panic: runtime error: index out of range ** with length **

fruits := []string{"apple", "orange", "grape"} 
fmt.Println("my favorite fruit is:", fruits[len(fruits)]) 
// panic: runtime error: index out of range [3] with length 3

注意索引index是小于等于len(slice)-1,而不是容量cap(slice)-1

numbers := make([]int, 3, 5) 
numbers[3] = 5 
//panic: runtime error: index out of range [3] with length 3

切片越界

        在通过切片操作(slice[low:high])创建新的slice时,如果low或high超出了原slice的界限,也会触发panic。

这样panic提示一般是这样的:

panic: runtime error: slice bounds out of range **

slice := []int{1, 2, 3} 
newSlice := slice[4:] 
// low超出原slice的界限,会触发panic

小结

        在某些情况下,对slice的底层数组进行不安全的操作(比如直接修改slice的结构体字段或使用unsafe包进行底层操作),可能会导致slice处于不一致的状态,进而引发panic。这种情况较为罕见,通常发生在底层编程或处理复杂数据结构的场景中,Go语言本身对slice操作有严格的类型检查和边界检查,因此大多数常见的错误在编译时就能被捕获。然而,由于运行时动态分配内存和扩展slice的特性,上述提到的几种情况仍可能导致运行时panic。为了避免这些问题,需要仔细检查slice的索引和切片操作,确保它们始终在有效范围内。

六、slice的相关操作

slice底层数组指针的获取方式

        为了调试方面、以及更好地说明slice的操作机制,有时候需要看到slice底层数组的位置

这里有三种方式可提供:

方式1、unsafe.SliceData

golang的标准库src\unsafe\unsafe.go里提供了一个很好的获取slice底层数组地址的函数

s0 := make([]int, 2, 3)
fmt.Println("\n\n", s0, "\n address :", unsafe.SliceData(s0), "\n len:", len(s0), "\n cap:", cap(s0))

输出结果:

方式2、%p

        slice是个包含底层数组指针,len,cap的值,是个值,和其他变量一样,获取slice的地址可以用&,同时%p在fmt文档中介绍,也有着特殊的作用,参考fmt的the documentation,可以看到

%p可以打印slice底层数组的第一个元素的地址,也就是slice底层数组的地址

示例:

s0 := make([]int, 2, 3)fmt.Printf("the address of the 0th element of the slice s0 :%p\n", s0)

输出结果:

方式3、reflect

示例:

s0 := make([]int, 2, 3)fmt.Printf("underlying array of slice addr=%#v\n", *((*reflect.SliceHeader)(unsafe.Pointer(&s0))))

输出结果:

slice的长度

内置的len函数

示例:

s0 := []int{10, 20, 30, 40, 50}
fmt.Println("length of s0:", len(s0))

运行结果:

slice的容量

内置的cap函数

示例:

s0 := []int{10, 20, 30, 40, 50}
fmt.Println("capcity of s0:", cap(s0))

运行结果:

slice的遍历

基于索引的遍历

s0 := []int{10, 20, 30, 40, 50}for i := 0; i < len(s0); i++ {fmt.Println(i, s0[i])
}

使用range实现遍历

s0 := []int{10, 20, 30, 40, 50}for index, value := range s0 {fmt.Println(index, value)
}

slice的截取

如图,通过对slice的截取运算,并不会重新分配内存片段,而是通过指针的移动实现,新旧切片指向底层数组的指针仍然指向同一个内存片段

示例:

var ptr *int // 声明一个int类型的指针 
fmt.Printf("\n pointer occupy size: %d bytes\n", unsafe.Sizeof(ptr)) s0 := []int{10, 20, 30, 40, 50} 
fmt.Println("\n\n", s0, "\n address :", unsafe.SliceData(s0), "\n len:", len(s0), "\n cap:", cap(s0)) s1 := s0[2:4] 
fmt.Println("\n\n", s1, "\n address :", unsafe.SliceData(s1), "\n len:", len(s1), "\n cap:", cap(s1))

运行结果:

slice的追加

内置append函数

func append(slice []Type, elems ...Type) []Type
  • slice 是Type类型的目标切片
  • elems 是要追加到切片末尾的元素,也是Type类型
  • append() 函数返回一个新的切片,依然是Type类型,其中包含了原始切片的所有元素以及追加的元素
append不扩容的情况

示例:

s0 := make([]int, 3, 5)fmt.Println("before append:s0 slice:", s0, " address of of the underlying array:", unsafe.SliceData(s0), " len(s0):", len(s0), "cap(s0):", cap(s0))s0 = append(s0, 1, 2)fmt.Println("after append:s0 slice:", s0, " address of of the underlying array:", unsafe.SliceData(s0), " len(s0):", len(s0), "cap(s0):", cap(s0))

运行结果:

 追加前后underlying array没有变化,还是指向同一个底层数组

 

append扩容的情况

示例:

s0 := make([]int, 3, 5)fmt.Println("before append:s0 slice:", s0, " address of of the underlying array:", unsafe.SliceData(s0), " len(s0)=", len(s0), "cap(s0)=", cap(s0))s0 = append(s0, 1, 2, 3)fmt.Println("after append:s0 slice:", s0, " address of of the underlying array:", unsafe.SliceData(s0), " len(s0)=", len(s0), "cap(s0)=", cap(s0))

运行结果:

append的返回值很重要

        一个slice传递给append函数,append会接受这个slice的一个副本,然后append内部对这个副本操作,注意这个副本包含了跟原始slice实参同一个底层数组的引用,

  • 如果不扩容,append返回这个副本的值,依然指向同原始slice实参同一个底层数组,容量没变,如果只是更改值,长度不变,如果是追加元素不扩容,则长度会变化,容量不变
  • 如果需要扩容,append函数内部会先算出一个合理的新的容量值,新建一个这个容量值的数组,然后将老元素拷贝到这个数组里,再将新元素追加到这个数组的后面,然后将计算后新的长度和新的容量作为新slice的len和cap,然后将这个新数组的地址作为slice的指针段,然后返回新的slice,之前的副本的slice里的指针被GC收回
  • 注意如果append内部在需要扩容的情况下,不返回这个新的slice,append的操作可能就被阉割隐匿了,是无效的;如果返回了,需要注意的是这个新的切片是否重新赋值给原始的切片变量,如果赋值给一个新的切片变量值,则是两个变量如下面的情况:
    slice := make([]init,3,5)slice = append(slice,1,2,3)
    //返回给调用者的同一个变量sliceNew := append(slice,1,2,3)
    //返回给新的切片变量//上面两种情况是两个不同的切片

append扩容依据及实现步骤:

  1. 检测切片容量:切片的扩容与不扩容的依据主要取决于当前切片的长度(len)和容量(cap)以及要追加的元素数量,当向切片追加元素时,如果追加后的长度(len(slice) + 追加元素数量)超过了当前容量(cap(slice)),切片就会发生扩容;如果追加后的长度不超过当前容量,切片就不会发生扩容,而是直接将新元素添加到切片的末尾,然后更新切片长度。
  2. 如果容量不够: 如果切片的容量不足以容纳新元素,append 将会执行扩容操作:
  3. 选择新容量:Go 将根据一定的策略选择一个新的容量。一般来说,新容量会比当前容量大,并且通常会选择一个相对较大的值,以减少后续的扩容次数,现有的选择新容量的依据是:原 slice 的容量小于 1024,则新 slice 的容量将扩大为原来的 2 倍;如果原 slice 的容量大于 1024,则新的 slice 的容量将扩大为原来的 1.25 倍;但不同版本可能会有不同的扩容策略,具体可参考src\runtime\slice.go里的growslice函数
  4. 分配新内存:一旦确定了新的容量,Go 将分配一个新的数组,其长度为新容量,然后将当前切片的所有元素复制到新的数组中,将新元素追加到新数组的末尾。
  5. 更新切片:最后,Go 更新原始切片的指向新数组的指针,并更新切片的长度,
  6. 返回更新后的切片。

注:

由于底层数组的更改,append 操作可能会导致原始切片的引用失效,因此通常情况下,我们会将结果分配给原始切片变量,以确保更新后的切片引用得到正确的处理。

slice =append(slice,10)

直接调用slice =append(slice,10)的时候也许会记得,将结果返回给原始变量,但是append在某个如change函数内部调用,而调用change函数时,往往忘记返回而造成错误,如

func addItemToSlice(s []int) {s = append(s, 10)
}

slice的拷贝

浅拷贝

        简单地将一个切片赋值给另一个变量并不会创建一个新的、独立的副本。相反,两个变量引用同一个底层数组,这意味着一个切片中的更改会反映在另一个切片中,

如果只是将src slice新赋值给一个新的dst变量,并不会创建一个新的,独立的副本,尽管两个变量都有这个各自的slice header,但是这两个值保存的内容是一样的,所以两个slice的array pointer是一样的,指向了一个底层数组,这种方式,修改一个 slice 的元素会影响到另一个 slice。

示例:

src := []int{10, 20, 30, 40, 50}
dst := srcfmt.Println("src slice:", src, " address of of the underlying array:", unsafe.SliceData(src), " len(src):", len(src), "cap(src):", cap(src))
fmt.Println("dst slice:", dst, " address of of the underlying array:", unsafe.SliceData(dst), " len(dst):", len(dst), "cap(dst):", cap(dst)

运行结果:

深拷贝

         深拷贝后源切片和目的切片的指针部分指向了独立的两个underlying array,修改其中一个切片,不会对另一个切片产生影响

内置copy函数

func copy(dst, src []Type) int

  • dst 是目标 slice

  • src 是源 slice

  • Type 是 slice 中元素的类型。

  • int是返回的实际复制的元素

        这个函数copy多少元素取决于源切片和目的切片长度的较小的值min(len(dst), len(src)),

如果目标切片为空或nil,则不会进行任何复制,需要注意的反而是src为nil的情况。

示例:

var src []int
dst := make([]int, len(src))copy(dst, src)
fmt.Println(src == nil, dst == nil) // true falsefmt.Println("src slice:", src, " address of of the underlying array:", unsafe.SliceData(src), " len(src):", len(src), "cap(src):", cap(src))
fmt.Println("dst slice:", dst, " address of of the underlying array:", unsafe.SliceData(dst), " len(dst):", len(dst), "cap(dst):", cap(dst))

运行结果:

想实现src=nil的时候,拷贝后dst=nil

则可以加个判断

var src []int
dst := make([]int, len(src))if src == nil {dst = nil
} else {copy(dst, src)
}
fmt.Println(src == nil, dst == nil) // true false
内置copy函数实现深拷贝

        我们想用内置copy函数实现深拷贝的话,需要新建目的切片的时候要用dst := make([]int, len(src))这种方式。

示例:

src := []int{10, 20, 30, 40, 50}
dst := make([]int, len(src))copy(dst, src)fmt.Println("src slice:", src, " address of of the underlying array:", unsafe.SliceData(src), " len(src):", len(src), "cap(src):", cap(src))
fmt.Println("dst slice:", dst, " address of of the underlying array:", unsafe.SliceData(dst), " len(dst):", len(dst), "cap(dst):", cap(dst))

运行结果:

内置append函数实现深拷贝

示例:

src := []int{10, 20, 30, 40, 50}
dst := append([]int{}, src...)fmt.Println("src slice:", src, " address of of the underlying array:", unsafe.SliceData(src), " len(src):", len(src), "cap(src):", cap(src))
fmt.Println("dst slice:", dst, " address of of the underlying array:", unsafe.SliceData(dst), " len(dst):", len(dst), "cap(dst):", cap(dst))
}

运行结果

七、slice在函数调用时是header传递

      我们平时在开发中遇到的值传递和引用传递的特点是:

  • 值传递的特点是:当使用值传递时,函数会接收参数的副本,而不是参数本身,意味着函数内部对参数的修改不会影响到原始实参的值
  • 引用传递的特点是:函数会接收参数的引用(即内存地址),而不是参数的副本,意味着在函数内部对参数的修改会影响到原始数据

      严格意义上将golang的函数传递是值传递,参数在传递给函数时,会复制一份副本传递给函数,只不过有的是普通的变量的副本,有的是包含了指针的变量的副本,而golang语言内部的、自动的、隐式的、引用或者解引用的对调用者不可见,往往给调用者造成困扰。

在我们给slice的函数传递方式定义为值传递或者引用传递之前,不妨先看两个示例:

change示例:

func change(s []int) {s[0] = 1fmt.Println("in change ", s)
}func main() {s0 := []int{10, 20, 30, 40, 50}fmt.Println("before change ", s0)change(s0)fmt.Println("after chang ", s0)}

运行结果:

上述的change示例,似乎slice是引用传递的结果,然后再看一个示例:

addItemToSlice示例

func addItemToSlice(s []int) {s = append(s, 10)}func main() {s0 := []int{10, 20, 30, 40, 50}fmt.Println("before change ", s0)addItemToSlice(s0)fmt.Println("after chang ", s0)}

运行结果:

 上述addItemToSlice示例,似乎是值传递的结果

分析:

slice的传递机制是这样的:前面说过slice是一个特殊的结构体

type slice struct {array unsafe.Pointerlen intcap int
}

        change函数接收了slice s0的一个副本,这个change内部对副本里指向的底层数组里元素更改,然后长度和容量没变化,函数返回,副本回收,原始实参s0的底层数组与副本指向是一样的,所以更改有效了。

        addItemToSlice函数接收了slice s0的一个副本,这个addItemToSlice内部调用了append函数,并append函数对s0进行了扩容,新建了一个更大容量的slice,但是这个append并没有将这个新的slice f返回,而传递给addItemToSlice的slice副本,在函数调用完也就被GC回收了,所以更改是无效的。具体解释也可以参考本文章的append返回值很重要这一节对append函数的解释

总结

        slice的函数传递时究竟是值传递还是引用传递,都是受限制,从语义上很容易误导,不能只从语义上简单归类于是值传递或者引用传递,否则会造成值传递不更改Slice,引用传递更改slice的错误结论,关键的是append函数是否将新的slice作为返回值返回给调用者,并且注意是否将这个新的切片返回给原来的切片变量,还是返回给了一个新的切片变量。

        规避这种误导的最好方式slice在函数传递时是header传递或者是slice header传递,在channel的传递时亦是如此。

相关文章:

golang slice总结

目录 概述 一、什么是slice 二、slice的声明 三、slice的初始化、创建 make方式创建 创建一个包含指定长度的切片 创建一个指定长度和容量的切片 创建一个空切片 创建一个长度和容量都为 0 的切片 new方式创建 短声明初始化切片 通过一个数组来创建切片 声明一个 …...

MySQL 数据库的优化

目录 一. 常见故障 单实例常见故障 1. 故障一 2. 故障二 3.故障三 4. 故障四 5. 故障五 6.故障六 7.故障七 8.故障八 主从环境常见故障 1.故障一 2. 故障二 3. 故障三 二. 优化 1.硬件方面 1.1 关于CPU 1.2 关于内存 1.3 关于磁盘 2. 配置文件优化 关于引擎…...

Redis 的主从复制、哨兵和cluster集群

目录 一. Redis 主从复制 1. 介绍 2. 作用 3. 流程 4. 搭建 Redis 主从复制 安装redis 修改 master 的Redis配置文件 修改 slave 的Redis配置文件 验证主从效果 二. Redis 哨兵模式 1. 介绍 2. 原理 3. 哨兵模式的作用 4. 工作流程 4.1 故障转移机制 4.2 主节…...

Unity进阶之路(2)UI Toolkit

UI Toolkit是Unity内置的一个游戏UI解决方案。借鉴了web前端的设计模式。 web前端使用css&#xff0c;html&#xff0c;js。 其中css定义样式 html定义层级 js处理逻辑 UI Toolkit则是使用uss&#xff0c;uxml&#xff0c;C# 如果直接使用Unity提供的可视化UI创建工具创建…...

实现Hello Qt 程序

&#x1f40c;博主主页&#xff1a;&#x1f40c;​倔强的大蜗牛&#x1f40c;​ &#x1f4da;专栏分类&#xff1a;QT❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 目录 一、使用 "按钮" 实现 1、纯代码方式实现 2、可视化操作实现 &#xff08;1&#xff09…...

若依 ruoyi-vue 接口挂载获取Resources静态资源文件权限校验

解决小程序图片打包过大&#xff0c;放置后端&#xff0c;不引用ngnix、minio等组件&#xff0c;还能进行权限校验 package com.huida.web.controller.common.app;import com.huida.common.core.controller.BaseController; import com.huida.common.utils.file.FileUtils; imp…...

【STM32嵌入式系统设计与开发】——16InputCapture(输入捕获应用)

这里写目录标题 STM32资料包&#xff1a; 百度网盘下载链接&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1mWx9Asaipk-2z9HY17wYXQ?pwd8888 提取码&#xff1a;8888 一、任务描述二、任务实施1、工程文件夹创建2、函数编辑&#xff08;1&#xff09;主函数编辑&#…...

「论文阅读」还在手写Prompt,自动Prompt搜索超越人类水平

每周论文阅读笔记&#xff0c;来自于2023LARGE LANGUAGE MODELS ARE HUMAN-LEVEL PROMPT ENGINEERS code:https://github.com/keirp/automatic_prompt_engineer 手写prompt确实很费脑筋&#xff0c;但其实本身大语言模型就是一个很好的自动prompt工具&#xff0c;APE文章提出自…...

安全测试概述和用例设计

一、安全测试概述 定义&#xff1a;安全测试是在软件产品开发基本完成时&#xff0c;验证产品是否符合安全需求定义和产品质量标准的过程。 概念&#xff1a;安全测试是检查系统对非法侵入渗透的防范能力。 准则&#xff1a;理论上来讲&#xff0c;只要有足够的时间和资源&a…...

JavaScript 超详细学习思路

JavaScript 是一种轻量级的编程语言&#xff0c;它可以在网页中嵌入&#xff0c;用来实现网页的动态效果和用户交互功能。它是 Web 开发中不可或缺的一部分&#xff0c;与 HTML 和 CSS 并称为 Web 技术的三大基石。下面我会根据您的要求&#xff0c;对每个部分进行详细的讲解。…...

LeetCode:1483. 树节点的第 K 个祖先(倍增 Java)

目录 1483. 树节点的第 K 个祖先 题目描述&#xff1a; 实现代码与解析&#xff1a; 倍增 原理思路&#xff1a; 1483. 树节点的第 K 个祖先 题目描述&#xff1a; 给你一棵树&#xff0c;树上有 n 个节点&#xff0c;按从 0 到 n-1 编号。树以父节点数组的形式给出&#…...

ConstraintLayout在复杂布局中,出现卡顿问题解决记录

ConstraintLayout在画界面的过程中&#xff0c;确实带来了不少的方便&#xff0c;随着使用的越来越多&#xff0c;也发现了一些问题&#xff0c;特此记录一下问题和解决方案。 在背景为图片&#xff0c;而背景图片宽度固定高度自适应的情况下&#xff0c;布局显示在图片固定位…...

责任链模式详解+代码案例

责任链设计模式 定义&#xff1a; 又名职责链模式&#xff0c;为了避免请求发送者与多个请求处理者耦合在一起&#xff0c;将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链&#xff1b;当有请求发生时&#xff0c;可将请求沿着这条链传递&#xff0c;直到…...

如何让Webots支持C#语言开发的控制器

Webots支持C、C、Java、Python、Matlab这五种语言开发控制器&#xff0c;没有直接支持C#&#xff0c;但有个同事已经用C#写了大量的机器人控制代码&#xff0c;想在不把C#代码改写成C的情况下&#xff0c;直接用webots仿真&#xff0c;那就得想想办法。(不过&#xff0c;让Chat…...

如何将本地仓库放到远程仓库中

在我们仓库创建好之后&#xff0c;我们复制好ssh 接着我们需要使用git remote add<shortname><url>这个命令 shortname就是我们远程仓库的别名 接着使用git remote -v这个命令查看一下目前远程仓库的别名和地址 原本还有一个指令git branch -M main 指定分支的名…...

Jedis-事务

一&#xff0c;Jedis 我们要使用Java来操作Redis Jedis是Redis官方推荐的java连接工具。使用Java操作Redis的中间件。如果你要使用Java操作redis&#xff0c;那么一定要对jedis十分的熟悉 二&#xff0c;idea 连接jedis 1&#xff0c;导入jar包 <dependencies><depen…...

智慧安防监控EasyCVR视频调阅和设备录像回看无法自动播放的原因排查与解决

智慧安防监控EasyCVR视频管理平台能在复杂的网络环境中&#xff0c;将前端设备统一集中接入与汇聚管理。国标GB28181协议视频监控/视频汇聚EasyCVR平台可以提供实时远程视频监控、视频录像、录像回放与存储、告警、语音对讲、云台控制、平台级联、磁盘阵列存储、视频集中存储、…...

百元不入耳蓝牙耳机哪个好?必入五款高性价比产品

喜欢听歌的朋友都会遇到一个常见问题&#xff0c;长时间戴耳机容易导致耳朵不适&#xff0c;甚至疼痛难忍。这种情况下&#xff0c;要是不听音乐反而感到不舒服&#xff0c;真是让人苦恼&#xff0c;仿佛音乐与耳机无法和谐共存。但是&#xff0c;难道就没有一款既舒适又让人心…...

android APP monkey 测试

monkey 测试 一、电脑ADB安装及使用详解1、什么是 Monkey 测试2、什么是ADB3、ADB的作用4、安装前提条件5、ADB下载6、ADB安装与配置 二、连接安卓手机检查是否连接上安卓手机windows端安装ADB驱动 三、 monkey测试操作指令演示指令APP包名查看方式测试效果 一、电脑ADB安装及使…...

IMBoy缓存系统深度解析:为何选择depcache而非ETS或Redis

在IMBoy即时通讯平台的开发过程中&#xff0c;我们面临了选择最佳缓存系统的关键技术决策。经过细致的考量&#xff0c;我们选择了depcache作为IMBoy的核心缓存机制。本文将阐述IMBoy缓存系统的选型理由&#xff0c;并对比分析depcache与纯ETS方案及Redis方案的不同优势。 1. …...

Twitter Api查询用户粉丝列表

如果大家为了获取实现方式代码的话可能要让大家失望了&#xff0c;这边文章主要是为了节省大家开发时间&#xff0c;少点坑。https://api.twitter.com/2/users/:id/followers &#xff0c;这个接口很熟悉吧&#xff0c;他是推特提供的获取用户关注者&#xff08;粉丝&#xff0…...

深入理解计算机系统 家庭作业 2.96

题目出的很不好,感觉没有标准. #include <stdio.h>typedef unsigned float_bits;int float_f2i(float_bits f) {unsigned sign f >> (31);unsigned exp (f >> 23) & 0xff;unsigned frac f & 0x7fffff;unsigned add (frac & 0x3) 0x3;unsig…...

主函数if __name__ == ‘__main__‘:

在Python中&#xff0c;主函数通常指的是脚本的入口点&#xff0c;也就是当你直接运行一个Python脚本时&#xff0c;会首先执行的函数。在大多数Python脚本中&#xff0c;主函数并不是通过main()这样的函数名来定义的&#xff0c;而是通过检查脚本是作为模块导入还是被直接运行…...

34.Python从入门到精通—Python3 正则表达式检索和替换

34.从入门到精通&#xff1a;Python3 正则表达式检索和替换 repl 参数是一个函数 正则表达式对象 正则表达式修饰符 - 可选标志 正则表达式模式* 正则表达式实例 检索和替换repl 参数是一个函数正则表达式对象正则表达式修饰符 - 可选标志正则表达式模式*正则表达式实例 检索和…...

springboot 反射调用ServiceImpl时报错:java.lang.NullPointerExceptio、,mapper为null【解决方法】

springboot 反射调用ServiceImpl时报错&#xff1a;java.lang.NullPointerException、mapper为null【解决方法】 问题描述问题分析解决方案创建SpringBootBeanUtil编写调用方法 executeMethod调用 总结 问题描述 在使用Spring Boot时&#xff0c;我们希望能够通过反射动态调用…...

内网安全之域内密码喷洒

域内密码喷洒一般和域内用户名枚举一起使用&#xff0c;可以在无域内凭据的情况下&#xff0c;通过枚举出域内存在的用户名&#xff0c;进而对域内存在的用户名进行密码喷洒&#xff0c;以此来获得域内有效凭据。 在Kerberos协议认证的AS-REQ阶段&#xff0c;请求包cname对应的…...

何为HTTP状态码?一文清楚基本概念。

在客户端与服务器之间的信息传输过程中&#xff0c;我们可以将其比喻为客户与快递员之间的包裹传递。那么服务器是如何通知客户端&#xff0c;操作是成功还是失败&#xff1f;或者有其他的一些情况呢&#xff1f;&#xff08;就像客户可以查询快递的状态&#xff09; 而这背后…...

SV学习笔记(七)

文章目录 类型转换写在前面动态转换子类句柄赋值于父类句柄父类句柄转换为子类句柄 虚方法写在前面非虚函数的调用虚函数的调用虚方法的建议为什么使用虚方法 对象拷贝写在前面赋值和拷贝总结 回调函数写在前面实例完成回调函数功能需要三步&#xff1a; 参数化类写在前面实现一…...

Windows SDK(五)按钮静态文本与编辑框控件

我们首先应该知道&#xff0c;所谓按钮静态文本等等控件都是窗口&#xff0c;他们都是隶属于父窗口下的子窗口&#xff0c;所 以在创建控件前&#xff0c;我们要首先创建一个父窗口&#xff0c;此处我们直接使用Windows桌面程序创建时&#xff0c;程 序自动为我们创建的一个窗…...

基于SSM框架实现的在线心理评测与咨询系统(技术栈 spring+springmvc+mybatis+jsp+jquery+css)

一、项目简介 本项目是一套基于SSM框架实现的在线心理评测与咨询系统&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Java学习者。 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&am…...

手机网站建设哪家公司好/高中同步测控优化设计答案

二分查找 33. 搜索旋转排序数组思路开始搜索旋转排序数组 例如&#xff0c; [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。 给你 旋转后 的数组 nums 和一个整数 target &#xff0c;如果 nums 中存在这个目标值 target &#xff0c;则返回它的下标&#x…...

ps和dw 做网站/谷歌play

bootstrap默认 html&#xff5b;font-size: 10px;} rem是一个相对大小的值&#xff0c;它相对于根元素<html>&#xff0c; 比如假设&#xff0c;我们设置html的字体大小的值为 html&#xff5b;font-size: 87.5%;&#xff5d;&#xff08;也就是14px&#xff09;。然后其…...

建设商务网站的经济可行性分析/百度竞价返点一般多少

参考链接 Java native Interface(JNI) Java本地接口 //加包名的话&#xff0c;最后要回到包下执行javah生成头文件命令 public class NativeInterface {/*** 这是一个native方法*/public native void sayHello(); }java源文件不加包名&#xff0c;可直接生成.h头文件&#xf…...

上海新冠疫情防控工作发布会/手机seo排名软件

如题&#xff0c;List集合添加一个List集合怎么操作 如上&#xff0c;现在有了List<A>,A类中有个字段List<B>, 新创建一个List<B>,想把LIst<A>中的每一个A.List<B>放到这个新创建的LIst<B>中 解决方法如下&#xff1a;...

wordpress 不显示侧边栏/网站关键词排名服务

参考 7.1.1 节 Boyd 和 Vandenberghe 《凸优化》 CVX示例库Logistic 回归示例Logistic 回归 考虑一个随机变量y∈{0,1}y\in\{0,1\}y∈{0,1}&#xff0c; prop(y1)pprop(y0)1−p\text{prop}(y1) p\\ \text{prop}(y0) 1-p prop(y1)pprop(y0)1−p 假设yyy取决于变量u∈Rnu\in\m…...

b2c网上购物商城网站/网络营销服务

在筛选条件中输入关键字&#xff0c;下方就会出现对应的结果&#xff0c;在产品报价&#xff0c;物料查询等多个场景都超实用&#xff1a;1、准备数据源数据源要规范&#xff0c;表头要在第一行&#xff0c;不要合并单元格&#xff0c;表头不要带有特殊字符&#xff0c;表头不能…...