【九】Golang 数组
💢欢迎来到张胤尘的技术站
💥技术如江河,汇聚众志成。代码似星辰,照亮行征程。开源精神长,传承永不忘。携手共前行,未来更辉煌💥
文章目录
- 数组
- 数组初始化
- 默认初始化
- 显式初始化
- 省略长度初始化
- 索引号初始化
- 内存布局
- 内存分配
- 常见操作
- 访问数组元素
- 修改数组元素
- 删除数组元素
- 获取数组长度
- 遍历数组
- 数组切片
- 数组排序
- 基本类型排序
- 结构体类型排序
- 实现`sort.Interface`接口进行自定义排序
- 直接切片排序
- 手动实现排序
- 数组合并
- 数组反转
- 数组去重
- 数组过滤
- 数组作为函数参数
- 指针传递数组
- 使用切片
- 多维数组
- 声明和初始化
- 访问和修改多维数组
- 遍历多维数组
数组
在 golang 中,数组是一种复合数据类型。数组的长度是一个常量,表示数组的大小,类型是数组中元素的数据类型,定义如下所示:
var 数组名 [长度]类型
定义一个长度为5的整型数组,代码如下所示:
var arr [5]int
另外在开发过程中使用数组需要注意以下规则:
- 数组长度固定:数组的长度在声明时必须确定,并且在程序运行过程中不能改变。
- 数组元素类型相同:数组中的所有元素必须是相同的数据类型。
数组初始化
默认初始化
如果在声明数组时没有初始化,数组中的所有元素都会被初始化为该类型的零值,代码如下所示:
不同数据类型的零值不同,具体请查看 《
Golang变量和常量》章节内容。
package mainimport "fmt"func main() {var arr [5]intfmt.Println(arr) // [0 0 0 0 0]
}
显式初始化
可以在声明时直接初始化数组元素,代码如下所示:
package mainimport "fmt"func main() {var arr [5]int = [5]int{1, 2, 3, 4, 5}fmt.Println(arr) // [1 2 3 4 5]
}
或者采用短声明的方式初始化数组,代码如下所示:
package mainimport "fmt"func main() {arr := [5]int{1, 2, 3, 4, 5}fmt.Println(arr) // [1 2 3 4 5]
}
如果初始化时提供的元素个数少于数组的长度,剩余的元素会被自动初始化为零值。如果初始化时提供的元素个数大于数组的长度则编译无法通过,提示错误。
package mainimport "fmt"func main() {arr1 := [5]int{1, 2, 3, 4} // 只提供了4个元素,但是数组长度是5,最后一个元素初始化为0值fmt.Println(arr1) // [1 2 3 4 0]// arr2 := [5]int{1, 2, 3, 4, 5, 6}// fmt.Println(arr2) 报错:golang/code/test.go:7:31: index 5 is out of bounds (>= 5)
}
省略长度初始化
如果在初始化数组时使用...,编译器则会根据初始化的元素个数自动推导数组的长度。
package mainimport "fmt"func main() {arr := [...]int{1, 2, 3}fmt.Println(arr) // [1 2 3]fmt.Println("数组长度:", len(arr)) // 数组长度: 3
}
索引号初始化
golang 中支持在声明数组时,为数组的某些特定索引位置指定初始值,语法格式如下:
[长度]类型{索引号1: 值1, 索引号2: 值2, ...}
或者不指定长度,采用 ... 省略长度初始化,让编译器进行自动类型推导,语法格式如下:
[...]类型{索引号1: 值1, 索引号2: 值2, ...}
例如:
package mainimport "fmt"func main() {numbers1 := [...]int{0: 10, 2: 30, 4: 50}fmt.Println(numbers1) // [10 0 30 0 50]fmt.Println(len(numbers1)) // 5numbers2 := [...]int{0: 10, 5: 30, 4: 50}fmt.Println(numbers2) // [10 0 0 0 50 30]fmt.Println(len(numbers2)) // 6
}
但是在使用过程中需要注意规则:
- 索引号必须是有效的:索引号必须是非负整数,并且不能超过数组的最大索引(即
长度 - 1)。否则,编译器会报错。 - 未指定的元素默认为零值:如果某些索引位置没有指定值,这些位置的元素会被自动初始化为对应类型的零值。
- 数组长度的确定:如果使用
...自动推断长度,编译器会根据最大的索引号加 1 来确定数组的长度。因此,初始化时必须确保索引号连续且不跳过。
内存布局
数组在内存中以连续的方式存储其元素,每个元素的内存地址是连续的并且可以通过内存地址加上偏移量进行计算。
同样的,数组的大小在编译时就已经确定,因此其所占内存大小也是固定的。
package mainimport "fmt"func main() {arr := [5]int{1, 2, 3, 4, 5}fmt.Printf("数组地址:%p\n", &arr) // 数组地址:0xc00001c210fmt.Printf("数组首元素地址:%p\n", &arr[0]) // 数组首元素地址:0xc00001c210
}
从上面代码打印结果可以看出,数组的地址和数组首元素的地址相等,那么也就是说数组第一个元素的地址,即数组的起始地址。
下面通过两种方式获取数组中每个元素的内存地址:直接遍历数组获取每个元素的地址、通过偏移量计算数组中每个元素的地址。通过两种方式可以更直观地验证数组元素在内存中的连续性,代码如下所示:
- 直接遍历数组获取每个元素的地址
package mainimport ("fmt""unsafe"
)func main() {arr := [5]int{1, 2, 3, 4, 5}// 数组首元素地址baseAddress := unsafe.Pointer(&arr[0])size := unsafe.Sizeof(arr[0])fmt.Println("数组元素字节大小:", size) // 数组元素字节大小: 8// 元素下标:0,地址:0xc00001c210// 元素下标:1,地址:0xc00001c218// 元素下标:2,地址:0xc00001c220// 元素下标:3,地址:0xc00001c228// 元素下标:4,地址:0xc00001c230// 遍历数组,打印每个元素的地址for i := 0; i < len(arr); i++ {// 直接打印每个元素的地址fmt.Printf("元素下标:%d,地址:%p\n", i, &arr[i])}
}
- 通过偏移量计算数组中每个元素的地址
package mainimport ("fmt""unsafe"
)func main() {arr := [5]int{1, 2, 3, 4, 5}// 数组首元素地址baseAddress := unsafe.Pointer(&arr[0])size := unsafe.Sizeof(arr[0])fmt.Println("数组元素字节大小:", size) // 数组元素字节大小: 8// 元素下标:0,地址:0xc00001c210// 元素下标:1,地址:0xc00001c218// 元素下标:2,地址:0xc00001c220// 元素下标:3,地址:0xc00001c228// 元素下标:4,地址:0xc00001c230for i := 0; i < len(arr); i++ {offset := uintptr(i) * uintptr(size)address := unsafe.Pointer(uintptr(baseAddress) + offset)fmt.Printf("元素下标:%d,地址:%p\n", i, address)}
}
内存分配
golang 的运行时系统会根据数组的大小和作用域自动选择栈分配或堆分配:
- 小数组(小于64 KB):通常分配在栈上,以减少内存分配的开销。
- 大数组或全局数组:分配在堆上,以避免栈溢出。
- 局部变量:如果数组是函数的局部变量且大小较小,通常分配在栈上。
- 全局变量:全局数组通常分配在堆上。
golang的内存分配机制基于垃圾回收(GC),不需要手动管理堆内存,简化了内存管理。更多关于
golang的内存机制请参考 《Golang内存模型》章节。
常见操作
访问数组元素
通过索引访问数组中的元素,数组的索引从0开始。
package mainimport "fmt"func main() {arr := [5]int{10, 20, 30, 40, 50}element := arr[2] // 访问索引为2的元素,值为30fmt.Println(element) // 30
}
修改数组元素
通过索引直接修改数组中的元素。
package mainimport "fmt"func main() {arr := [5]int{10, 20, 30, 40, 50}arr[2] = 300 // 修改索引为2的元素为300fmt.Println(arr) // [10 20 300 40 50]
}
删除数组元素
在 golang 中,数组的大小是固定的,无法直接删除数组中的元素。通常需要借助切片的特性来实现类似“删除元素”的操作。
package mainimport "fmt"func main() {arr := []int{1, 2, 3, 4, 5}// 删除的目标索引index := 2arr = append(arr[:index], arr[index+1:]...)fmt.Println(arr) // [1 2 4 5]
}
或者采用数组拷贝删除法,创建一个新的数组,将原数组中不需要删除的元素拷贝到新数组中。代码如下所示:
package mainimport "fmt"func main() {array := [5]int{0, 1, 2, 3, 4}// 删除的目标索引index := 2newArray := [4]int{}for i, j := 0, 0; i < len(array); i++ {if i != index {newArray[j] = array[i]j++}}fmt.Println(newArray) // [0 1 3 4]
}
或者直接通过 copy 函数将原数组中不需要删除的元素拷贝到新的切片中。代码如下所示:
package mainimport "fmt"func main() {arr := []int{1, 2, 3, 4, 5}// 删除的目标索引index := 2newarr := make([]int, len(arr)-1) // 创建一个新的切片copy(newarr, arr[:index])copy(newarr[index:], arr[index+1:])fmt.Println(newarr) // [1 2 4 5]
}
获取数组长度
使用len() 或者 cap() 函数获取数组的长度。
package mainimport "fmt"func main() {arr := [5]int{10, 20, 30, 40, 50}fmt.Println(len(arr)) // 5fmt.Println(cap(arr)) // 5
}
在
golang语言中,len和cap是用于描述数组和切片的两个重要函数,但是两者之间的含义和用途有所不同,尤其是在切片中,这种区别更为明显。对于数组而言,
len和cap的值是相同的,因为数组的长度是固定的,其容量就是数组的长度。在 《Golang切片》章节中进行详细讨论
遍历数组
可以使用 for 循环或 for range 循环遍历数组。
package mainimport "fmt"func main() {arr := [5]int{10, 20, 30, 40, 50}// 10// 20// 30// 40// 50// 使用 for 循环for i := 0; i < len(arr); i++ {fmt.Println(arr[i])}// Index: 0, Value: 10// Index: 1, Value: 20// Index: 2, Value: 30// Index: 3, Value: 40// Index: 4, Value: 50// 使用 for-range 循环for index, value := range arr {fmt.Printf("index: %d, value: %d\n", index, value)}
}
数组切片
通过切片操作可以获取数组的一个子集。
package mainimport "fmt"func main() {arr := [5]int{10, 20, 30, 40, 50}sub := arr[1:3] // 获取从索引1到索引3(不包括3)的子数组fmt.Println(sub) // [20 30]
}
数组排序
golang 中数组本身没有直接排序的方法,但是 sort 包下提供了强大的排序的功能,适用于基本类型(如int、string等)和复杂类型(如结构体数组)的排序。
基本类型排序
package mainimport ("fmt""sort"
)func main() {arr := [5]int{10, 20, 30, 40, 50}sort.Ints(arr[:]) // 将数组转换为切片后排序fmt.Println(arr) // [10 20 30 40 50]
}
另外 sort 包中提供了 sort.Slice 函数允许通过自定义比较函数来实现排序逻辑。代码如下所示:
package mainimport ("fmt""sort"
)func main() {arr := [5]int{10, 20, 30, 40, 50}sort.Slice(arr[:], func(i, j int) bool { // Slice函数的第二个参数中传递自定义排序函数return arr[i] > arr[j] // 降序排序})fmt.Println(arr) // [50 40 30 20 10]
}
结构体类型排序
实现sort.Interface接口进行自定义排序
package mainimport ("fmt""sort"
)type Person struct {Name stringAge int
}type ByAgeDesc []Personfunc (a ByAgeDesc) Len() int { return len(a) }
func (a ByAgeDesc) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByAgeDesc) Less(i, j int) bool { return a[i].Age > a[j].Age } // 降序排序func main() {people := [3]Person{{"Alice", 30},{"Bob", 25},{"Charlie", 35},}sort.Sort(ByAgeDesc(people[:]))fmt.Println(people) // 输出: [{Charlie 35} {Alice 30} {Bob 25}]
}
首先定义了一个 Person 结构体,包含两个字段:Name(字符串类型)和 Age(整型)。接着定义了一个新的类型ByAgeDesc,它是[]Person(Person结构体切片)的别名。这个类型将实现sort.Interface接口,从而可以使用sort.Sort函数对Person切片进行排序。
为了使用sort.Sort函数,需要实现sort.Interface接口的三个方法:Len、Swap和Less。
Len():返回切片的长度,即Person结构体切片中元素的数量。Swap():交换切片中索引为i和j的两个元素,用于在排序过程中交换元素的位置。Less():这个方法决定了排序的顺序,如果索引为i的元素的Age字段大于索引为j的元素的Age字段返回true,否则返回false
最后在主函数中创建了一个 Person 结构体数组 people,并初始化了三个 Person 元素。调用 sort.Sort 函数,将 people 通过 [:] 转换为切片后再转换为 ByAgeDesc 类型,sort.Sort 会根据 ByAgeDesc 类型实现的 sort.Interface 接口方法(Len、Swap和Less)对切片进行排序。
直接切片排序
直接操作切片,通过一个比较函数实现排序逻辑。代码简洁,适合快速实现排序需求。
package mainimport ("fmt""sort"
)type Person struct {Name stringAge int
}func main() {people := [4]Person{{"Alice", 30},{"Bob", 25},{"Charlie", 35},{"David", 30},}sort.Slice(people[:], func(i, j int) bool { // 自定义排序函数if people[i].Age == people[j].Age {return people[i].Name < people[j].Name // 名字升序}return people[i].Age > people[j].Age // 年龄降序})fmt.Println(people) // [{Charlie 35} {Alice 30} {David 30} {Bob 25}]
}
手动实现排序
该小结省略,后续在 《数据结构与算法》中针对常见的排序算法进行总结。
数组合并
在 golang 中由于数组的长度是固定的,因此不能直接合并两个数组。但可以通过切片和 append() 来实现类似的效果。代码如下所示:
package mainimport "fmt"func main() {arr1 := [3]int{1, 2, 3}arr2 := [2]int{4, 5}merged := append(arr1[:], arr2[:]...)fmt.Println(merged) // [1 2 3 4 5]
}
数组反转
golang 中数组没有直接提供反转元素的能力,通过遍历数组并交换元素来实现数组的反转。代码如下所示:
package mainimport ("fmt"
)func main() {arr := [5]int{1, 2, 3, 4, 5}for i := 0; i < len(arr)/2; i++ {arr[i], arr[len(arr)-1-i] = arr[len(arr)-1-i], arr[i]}fmt.Println(arr) // [5 4 3 2 1]
}
数组去重
golang 中数组没有直接提供去重元素的能力,但是可以通过切片和映射来实现。代码如下所示:
package mainimport "fmt"func main() {arr := [5]int{1, 2, 2, 3, 4}unique := make(map[int]bool) // 创建一个映射var result []intfor _, v := range arr {if !unique[v] { // 只有当映射中不存在时,才会将元素加入到结果集中unique[v] = trueresult = append(result, v)}}fmt.Println(result) // [1 2 3 4]
}
映射相关的知识点后续在 《
Golang映射》中进行总结。
数组过滤
通过遍历数组并根据条件筛选元素。代码如下所示:
package mainimport "fmt"func main() {arr := [5]int{1, 2, 3, 4, 5}var filtered []intfor _, v := range arr {if v%2 == 0 { // 只获取偶数的元素filtered = append(filtered, v)}}fmt.Println(filtered) // [2 4]
}
数组作为函数参数
在 golang 中,数组作为函数参数时的行为与其他类型有所不同。数组是值类型,这意味着将数组作为参数传递给函数时,函数内部会接收到数组的一个副本,而不是原始数组的引用。因此,对数组副本的修改不会影响原始数组。
以下是一个示例代码,展示数组作为函数参数时的行为:
package mainimport "fmt"func modifyArray(arr [5]int) {arr[0] = 100 // 修改数组的第一个元素fmt.Println(arr) // [100 2 3 4 5]
}func main() {numbers := [5]int{1, 2, 3, 4, 5}fmt.Println(numbers) // [1 2 3 4 5]modifyArray(numbers)fmt.Println(numbers) // [1 2 3 4 5]
}
在 modifyArray 函数中,arr 是 numbers 数组的一个副本。对 arr 的修改不会影响原始数组 numbers。在 main 函数中,numbers 数组的内容保持不变,即使在 modifyArray 中对其副本进行了修改。
如果希望在函数中修改原始数组,可以通过以下两种方式实现:
指针传递数组
通过传递数组的指针,函数可以直接修改原始数组。代码如下所示:
package mainimport "fmt"func modifyArray(arr *[5]int) {arr[0] = 100 // 修改数组的第一个元素fmt.Println(*arr) // [100 2 3 4 5]
}func main() {numbers := [5]int{1, 2, 3, 4, 5}fmt.Println(numbers) // [1 2 3 4 5]modifyArray(&numbers)fmt.Println(numbers) // [100 2 3 4 5]
}
使用切片
切片是引用类型,传递切片时,函数内部对切片的修改会直接影响原始切片。如果需要修改数组的内容,可以先将数组转换为切片,然后传递切片。代码如下所示:
package mainimport "fmt"func modifySlice(slice []int) {slice[0] = 100 // 修改切片的第一个元素fmt.Println(slice) // [100 2 3 4 5]
}func main() {numbers := [5]int{1, 2, 3, 4, 5}fmt.Println(numbers) // [1 2 3 4 5]modifySlice(numbers[:])fmt.Println(numbers) // [100 2 3 4 5]
}
多维数组
在 golang 中,多维数组是通过数组的数组来实现的。维数组的每个维度都有固定的大小,同样的多维数组在内存中是连续存储。
多维数组很多特性、操作和一维数组一致,不再赘述。下面只针对重点进行详细说明。
声明和初始化
声明一个 3x3 的二维数组:
var matrix [3][3]int
声明一个 2x3x2 的三维数组:
var arr [2][3][2]int
同样可以使用短声明的方式声明多维数组,让编译器进行自动类型推导:
package mainimport "fmt"func main() {matrix := [3][3]int{{1, 2, 3},{4, 5, 6},{7, 8, 9},}fmt.Println(matrix) // [[1 2 3] [4 5 6] [7 8 9]]
}
访问和修改多维数组
访问和修改多维数组的元素可以通过索引来进行操作。
package mainimport "fmt"func main() {matrix := [3][3]int{{1, 2, 3},{4, 5, 6},{7, 8, 9},}// 访问元素fmt.Println(matrix[1][2]) // 6// 修改元素matrix[1][2] = 100fmt.Println(matrix[1][2]) // 100fmt.Println(matrix) // [[1 2 3] [4 5 100] [7 8 9]]
}
遍历多维数组
通过嵌套的 for 循环来遍历多维数组。例如:
package mainimport "fmt"func main() {matrix := [3][3]int{{1, 2, 3},{4, 5, 6},{7, 8, 9},}// matrix[0][0] = 1// matrix[0][1] = 2// matrix[0][2] = 3// matrix[1][0] = 4// matrix[1][1] = 5// matrix[1][2] = 6// matrix[2][0] = 7// matrix[2][1] = 8// matrix[2][2] = 9// 遍历二维数组for i := 0; i < len(matrix); i++ {for j := 0; j < len(matrix[i]); j++ {fmt.Printf("matrix[%d][%d] = %d\n", i, j, matrix[i][j])}}
}
通过嵌套的 for range 循环来遍历多维数组。例如:
package mainimport "fmt"func main() {matrix := [3][3]int{{1, 2, 3},{4, 5, 6},{7, 8, 9},}// matrix[0][0] = 1// matrix[0][1] = 2// matrix[0][2] = 3// matrix[1][0] = 4// matrix[1][1] = 5// matrix[1][2] = 6// matrix[2][0] = 7// matrix[2][1] = 8// matrix[2][2] = 9// 遍历二维数组for i, arr := range matrix {for j, e := range arr {fmt.Printf("matrix[%d][%d] = %d\n", i, j, e)}}
}
🌺🌺🌺撒花!
如果本文对你有帮助,就点关注或者留个👍
如果您有任何技术问题或者需要更多其他的内容,请随时向我提问。

相关文章:
【九】Golang 数组
💢欢迎来到张胤尘的技术站 💥技术如江河,汇聚众志成。代码似星辰,照亮行征程。开源精神长,传承永不忘。携手共前行,未来更辉煌💥 文章目录 数组数组初始化默认初始化显式初始化省略长度初始化索…...
百达翡丽(Patek Philippe):瑞士制表的巅峰之作(中英双语)
百达翡丽(Patek Philippe):瑞士制表的巅峰之作 在钟表界,百达翡丽(Patek Philippe) 一直被誉为“世界三大名表”之一,并且常被认为是其中的至高存在。一句“没人能真正拥有一枚百达翡丽&#x…...
【学习】软件测试中的分类树法介绍
分类树法是一种软件测试设计技术,它通过构建一个树状结构来组织和展示输入数据的多种组合。这种方法有助于系统地识别和分析可能的测试情况,从而确保对软件进行全面而详尽的测试。分类树法特别适用于具有多个选择或条件的复杂系统,它可以有效…...
打造智能语料库:通过Coco AI Server 实现 Notion 笔记 RAG 检索功能
本文将详细介绍如何将 Notion 作为语料库,部署 Coco Server 的 RAG(Retrieval-Augmented Generation)功能。我们将使用 Easysearch 作为语料库存储 Notion 素材,并通过 ollama 进行 LLM 推理。 1. 环境准备 1.1 启动 Easysearch…...
SP字体UI放大代码
代码: echo off set QT_SCALE_FACTOR放大倍数 start "" "你的SP.exe启动路径"...
spring boot知识点2
1.spring boot 要开启一些特性,可通过什么方式开启 a.通过Enable注解,可启动定时服务 b.通过application.properties可设置端口号等地址信息 2.什么是热部署,以及spring boot通过什么方式进行热部署 热部署这个概念,我知道。就…...
动手学Agent——Day2
文章目录 一、用 Llama-index 创建 Agent1. 测试模型2. 自定义一个接口类3. 使用 ReActAgent & FunctionTool 构建 Agent 二、数据库对话 Agent1. SQLite 数据库1.1 创建数据库 & 连接1.2 创建、插入、查询、更新、删除数据1.3 关闭连接建立数据库 2. ollama3. 配置对话…...
qt实习总结
创建一个滑动条 QSlider *slider new QSlider(Qt::Vertical); //创建一个垂直方向的 进度条 带有上下箭头的输入框 QSpinBox 提供了一个带有上下箭头的输入框 垂直 水平怎么说 horizontal vetical 布局知识 BtnLayout->addWidget(AmendBtn); BtnLayout->addWidg…...
SpringBoot3.x整合WebSocket
SpringBoot3.x整合WebSocket 本文主要介绍最新springboot3.x下如何整合WebSocket. WebSocket简述 WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议,它允许在浏览器和服务器之间进行实时的、双向的通信。相对于传统的基于请求和响应的 HTTP 协议ÿ…...
vLLM专题(二):安装-CPU
vLLM 是一个 Python 库,支持以下 CPU 变体。选择您的 CPU 类型以查看供应商特定的说明: Intel/AMD x86 vLLM 最初支持在 x86 CPU 平台上进行基本模型推理和服务,支持的数据类型包括 FP32、FP16 和 BF16。 注意 此设备没有预构建的 wheel 包或镜像,因此您必须从源代码构建 v…...
「软件设计模式」适配器模式(Adapter)
软件设计模式深度解析:适配器模式(Adapter)(C实现) 一、模式概述 适配器模式(Adapter Pattern)是结构型设计模式中的"接口转换器",它像现实世界中的电源适配器一样&#…...
Dify平台搭建面试机器人
无代码搭建面试机器人 什么是Dify 什么是Dify Dify 是一款开源的大语言模型(LLM) 应用开发平台。它融合了后端即服务(Backend as Service)和 LLMOps 的理念,使开发者可以快速搭建生产级的生成式 AI 应用。即使你是非技术人员,也能…...
Vue 3 中可读可写的计算属性(Computed Properties)的使用场景
在 Vue 3 中,计算属性(Computed Properties)是一种基于响应式依赖进行缓存的属性。它们通常用于处理复杂的逻辑,并且只有当依赖的响应式数据发生变化时,才会重新计算。计算属性非常适合用于处理模板中的复杂表达式&…...
如何通过AI轻松制作PPT?让PPT一键生成变得简单又高效
如何通过AI轻松制作PPT?让PPT一键生成变得简单又高效!在这个信息化飞速发展的时代,PPT已经成为我们日常工作、学习和生活中不可或缺的一部分。无论是公司会议、学术报告,还是个人展示,PPT的作用都不容忽视。很多人对于…...
从零开始部署DeepSeek:基于Ollama+Flask的本地化AI对话系统
从零开始部署DeepSeek:基于OllamaFlask的本地化AI对话系统 一、部署背景与工具选型 在AI大模型遍地开花的2025年,DeepSeek R1凭借其出色的推理能力和开源特性成为开发者首选。本文将以零基础视角,通过以下工具链实现本地化部署: …...
STM32 CubeMx配置串口收发使用DMA并调用Idle模式(二)
本篇主要结合代码落实,之前串口已经配置好的DMA方式。 一、首先我们把串口看成一个对象,它有属性、私有数据和方法; 每个串口都有名字属性;有初始化、发送、接收方法;还有一个私有数据(这个私有数据是每个…...
使用Redis实现分布式锁,基于原本单体系统进行业务改造
一、单体系统下,使用锁机制实现秒杀功能,并限制一人一单功能 1.流程图: 2.代码实现: Service public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderSe…...
数据结构中的邻接表
一、概念 邻接表(Adjacency List)是一种用于表示图(Graph)数据结构的常用方法。它特别适用于稀疏图,即边的数量远小于顶点数量平方的图。邻接表通过为每个顶点维护一个列表来存储与该顶点相邻的顶点,从而高…...
js第九题
题九:放大镜效果 要求: 1.鼠标移至图片上方,鼠标周围出现黄色的的正方形框,黄色矩形 框会随着鼠标的移动而移动; 2.将黄色正方形框里的内容的长和宽均放大2.4倍,并在图片右边进 行显示。 html <div …...
基于单片机ht7038 demo
单片机与ht7038 demo,三相电能表,电量数据包括电流电压功能,采用免校准方法 列表 ht7038模块/CORE/core_cm3.c , 17273 ht7038模块/CORE/core_cm3.h , 85714 ht7038模块/CORE/startup_stm32f10x_hd.s , 15503 ht7038模块/CORE/startup_stm32…...
轮播图html
题十二:轮播图 要求: 1.鼠标不在图片上方时,进行自动轮播,并且左右箭头不会显示;当鼠标放在图片上方时,停止轮播,并且左右箭头会显示; 2.图片切换之后,图片中下方的小圆…...
Nginx内存池源代码剖析----ngx_create_pool函数
ngx_create_pool 是 Nginx 内存池 的初始化函数,负责创建并初始化一个内存池对象。它的作用是 为后续的内存分配操作提供统一的管理入口,通过预分配一块较大的内存区域,并基于此区域实现高效的内存分配、对齐管理和资源回收。 源代码定义&…...
DeepSeek 开放平台无法充值 改用其他平台API调用DeepSeek-chat模型方法
近几天DeepSeek开放平台无法充值目前已经关闭状态,大家都是忙着接入DeepSeek模型 ,很多人想使用DeepSeek怎么办? 当然还有改用其他平台API调用方法,本文以本站的提供chatgpt系统为例,如何修改DeepSeek-chat模型API接口…...
QT基础一、学会建一个项目
注:因为CSDN有很多付费才能吃到的史,本人对此深恶痛绝,所以我打算出一期免费的QT基础入门专栏,这是QT基础知识的第一期,学会建一个项目,本专栏是适用于c / c基础不错的朋友的一个免费专栏,接下来…...
科技引领未来,中建海龙C-MiC 2.0技术树立模块化建筑新标杆
在建筑行业追求高效与品质的征程中,中建海龙科技有限公司(简称“中建海龙”)以其卓越的创新能力和强大的技术实力,不断书写着装配式建筑领域的新篇章。1 月 10 日,由深圳安居集团规划,中建海龙与中海建筑共…...
解锁养生秘籍,拥抱健康生活
在这个快节奏的时代,人们行色匆匆,常常在忙碌中忽略了健康。其实,养生并非遥不可及,它就藏在生活的细微之处,等待我们去发现和实践。 规律作息是健康的基础。日出而作,日落而息,顺应自然规律&am…...
STM32 如何使用DMA和获取ADC
目录 背景 摇杆的原理 程序 端口配置 ADC 配置 DMA配置 背景 DMA是一种计算机技术,允许某些硬件子系统直接访问系统内存,而不需要中央处理器(CPU)的介入,从而减轻CPU的负担。我们可以通过DMA来从外设…...
细胞计数专题 | LUNA-FX7™新自动对焦算法提高极低细胞浓度下的细胞计数准确性
现代细胞计数仪采用自动化方法,在特定浓度范围内进行细胞计数。其上限受限于在高浓度条件下准确区分细胞边界的能力,而相机视野等因素则决定了下限。在图像中仅包含少量可识别细胞或特征的情况下,自动对焦可能会失效,从而影响细胞…...
DeepSeek教unity------MessagePack-01
中文:GitCode - 全球开发者的开源社区,开源代码托管平台 MessagePack是C# 的极速 MessagePack 序列化器。它比 MsgPack-Cli 快 10 倍,并且性能超过其他 C# 序列化器。MessagePack for C# 还内置支持 LZ4 压缩——一种极其快速的压缩算法。性能在诸如游戏…...
vite+vue3开发uni-app时低版本浏览器不支持es6语法的问题排坑笔记
重要提示:请首先完整阅读完文章内容后再操作,以免不必要的时间浪费!切记!!!在使用vitevue3开发uni-app项目时,存在低版本浏览器不兼容es6语法的问题,如“?.” “??” 等。为了方便…...
