Go语言——复合类型 上

简介: Go语言——复合类型 上

1. 数组

a.【声明数组

var name [size] type
// 如果数组长度不确定,可以使用 ... 代替数组的长度,
// 编译器会根据元素个数自行推断数组的长度:
balance := [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}

b.【初始化数组】

name := [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
// 1. 方式一:完整写法
  var arr [3]int = [3]int{1, 2, 3}
// 2. 方式二:短变量方式(常用)
  arr2 := [3]int{1, 2, 3}
// 3. 方式三:长度大于初始值个数.长度为4,只给前三个元素赋值,其余元素为默认值
  arr3 := [4]int{1, 2, 3}
// 4. 方式四:赋值时不写长度,数组长度根据元素个数确定(常用)
  arr4 := [...]int{1, 2, 3}
// 还可以通过指定下标来初始化元素
name := [...]float32{0:1000.0, 2:2.0, 3.4, 7.0, 50.0}

c.【数组作为参数传递】

// 正常情况下 数组为值传递 即传递的是数组的值 在函数中对数组的操作 对于原数组无效。
func modify(array [5]int) {
  fmt.Println (array) // [1 2 3 4 5]
  array[0] = 10
  fmt.Println(array) // [10 2 3 4 5]
}
func main() {
  array := [5]int{1, 2, 3, 4, 5}
  modify(array)
  fmt.Println (array) // [1 2 3 4 5]
}
// 将数组结合指针作为参数传递 可以利用指针达到引用传递的效果
func modify(array *[5]int) {
  fmt.Println(*array) // [1 2 3 4 5]
  (*array)[0] = 10
  fmt.Println(*array) // [10 2 3 4 5]
}
func main() {
  array := [5]int{1, 2, 3, 4, 5}
  modify(&array)
  fmt.Println(array) // [10 2 3 4 5]
}

d.【冒泡排序】

/*
  数组练习:冒泡排序
  注意:
    数组与切片的区别
*/
func main() {
  arr := [6]int{2,1,0,4,8,6}
  fmt.Println(BubbleSort(arr))
}
func BubbleSort(arr[6]int) [6]int {
  for i := 0; i < len(arr); i++ {
    for i1 := 0; i1 < len(arr)-i-1; i1++ {
      if arr[i1]<arr[i1+1] {
        continue
      }else {
        arr[i1],arr[i1+1] = arr[i1+1],arr[i1]
      }
    }
  }
  return arr
}

2. 二维数组

二维数组表示一个数组变量中每个元素又是一个一维数组变量,跟java一样

声明二维数组:

var name [n] [m] 
// 使用和java一样  n为行 m为列

数组的声明与赋值:

// 1.方式一:完整写法
var arr [3][3]int = [3][3]int{{1,2,3},{4,5,6},{7,8,9},}
// 2.方式二:短变量方式(常用) 注意 这里每行后都要有个逗号
arr := [3][3]int{
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9},
}

注意:

golang中的二维数组和java不一样 java不可以直接打印二维数组 但是go可以。

3. 指针

所谓的指针就是一个地址 而*指针 就相当于操作指针指向的那段地址的内存。

a.【变量地址】

变量本质就是内存中一块数据的标记,把值存储到变量中实质是把值存储到内存中

每次对变量重新赋值就是在修改对应变量地址中的内容

重新创建一个非引用型变量(即使是把已有变量直接赋值给新变量)也会新开辟内存地址

b.【指针变量】

指针变量指向一个值的内存地址

使用&+变量 返回值就是一个指针类型

声明指针不会开辟内存地址,只是准备指向内存某个空间,

而声明变量会开辟内存地址,准备存放内容.

所以指针类型变量都是把一个变量的地址赋值给指针变量。

使用 *+指针 能够获取 和 操作 内存地址中的值 所以 *+指针 == 直接使用变量。

应用指针可以实现多个地方操作同一个内存地址的值(在方法参数应用指针较多)

c.【指针的声明与赋值】

使用var变量名 *类型、 声明指针类型变量

【声明格式】

*var name type

*号用于指定变量是作为一个指针。

func main() {
    // 声明一个指针变量
  var a *int
  b:=1
    // 将指针指向变量b的内存地址
  a=&b
  *a = 2
  fmt.Println(b) // 2
}

使用new函数去创建指针,

使用new创建的指针是已经有指向的,所以可以直接通过 *指针 直接赋值;

而只声明的指针变量是不可以直接通过 *指针直接赋值的(野指针)。

new(type)会根据你给的type去开辟一个type大小空间 使指针指向这片空间,且不同于c语言 go语言的gc(垃圾回收)机制会自动释放这片空间。

【语法格式】

变量名 = new(type)

// 不同于直接声明指针,使用new(type) 声明的指针有一个默认的
var ptr01 *int
fmt.Printf("%p\n",ptr01) // 0x0
ptr02 := new(int)
fmt.Printf("%p\n",ptr02) // 0xc0000ac080

d.【空指针与野指针】

指针的默认值为 即:空指针

指针变量指向一个未知的空间 即为:野指针

// 空指针
var ptr01 *int
fmt.Println(ptr01) // nil
fmt.Printf("%p\n",ptr01) // 0x0
// 野指针: 指针变量指向一个未知的空间 
*ptr01 = 1 // 会报错

e.【指针与数组 切片 map 结构体】

通过指针间接操作(数组 切片 map 结构体)的方法

type Student struct {
  id int
}
func main() {
    // 与 数组 
    arr:=[3]int{1,2,3}
  s:=&arr
    // 方法一:(*变量名)[index]
  fmt.Println( (*s)[0] )
    // 方法二:变量名[index]
    fmt.Println( s[0] )
    // 与 结构体
  student := Student{1}
  i:=new(Student)
  i = &student
  i.id=2
  fmt.Println(student) // 2 
}

f.【指针作为参数传递】

指针作为参数传递为 引用传递

func main() {
  a := new(int)
  *a = 1
  test(a)
  fmt.Println(*a)
}
func test(a *int) {
  *a = 2
}

g.【多级指针】

  1. 指针本身也是值,且这个值和 12 一样,是不可以寻址的,这也是为什么 &(&a) 不行,但是 先b = &a,再&b 却可以。
func main() {
  s :=1
  p:= &s
  fmt.Println(a) // 变量的地址 0xc0000ac058
  fmt.Printf("%p",&p) // 一级指针的地址 0xc0000d8018
  pp:=&p
  fmt.Printf("%T\n",p) // *int 一级指针:指向变量的地址
  fmt.Printf("%T\n",pp) // **int 二级指针:指向一级指针的地址
}

通过二级指针 修改一级指针和变量的值

func main() {
  s := 1
  p := &s
  s1 := 2
  pp := &p
  *pp = &s1   // 相当于将一级指针的指向 从s 改成了 s1
  fmt.Println(*p) // 2
    **pp = 100   // 直接修改变量的值
  fmt.Println(s1) // 100
}

4. 切片

1.  切片的英文名称 slice
2.  切片:具有 可变长度 **相同类型**元素序列.
3.  由于长度是可变,可以解决数组长度在数据个数不确定情况下浪费内存的问题.
4.  切片和数组声明时语法最主要的区别就是长度
5.  切片只声明时为nil,没有开辟内存空间,不能直接操作切片,需要先初始化

注意:切片只能和nil进行判断是否相等

a.【切片是引用类型】

引用类型在变量之间赋值时传递的是地址.引用类型变量就是这个类型的指针.切片就是引用类型。

值类型在变量之间赋值时传递的是值的副本(就是复制了值 但是内存地址是重新开辟的)

b.【切片的声明与赋值】

    var s1 []int //声明切片和声明array一样,只是少了长度,此为空(nil)切片
    s2 := []int{}

make 函数

Go语言中可以使用make函数创建 slice、map、 channel、 interface

使用make函数定义 无内容,但不是nil 的切片,意味着切片已经申请了内存空间

语法格式:

make(类型,初始长度[,初始容量])

初始容量可以省略,默认和长度相等

长度 表示切片中元素的实际个数, 容量 表示切片占用空间大小, 且切片成倍增加.当增加到1024后按照一定百分比增加。

可以使用len() cap() 查看长度和容量

注意 长度和容量的不同

s := make([]int,1,3)
fmt.Printf("%p",&s) // 0xc000004078
fmt.Println(len(s))  // 1
fmt.Println(cap(s))  // 3
//  长度和容量的不同 
s := make([]int,0,3) // 此时有三个空位
s := make([]int,3,3) // 此时三个位置都是默认值 0

append 函数

func append(slice []Type, elems …Type) []Type

slice —>

slice = append(slice, elem1, elem2) 或 slice = append(slice, anotherSlice…)

可以向切片中添加一一个或多个值,添加后 必须使用切片接收append()函数返回值

如果添加一次 添加多个值,且添加后的长度大于扩容一次的大小,容量和长度相等.等到下次添加内容时如果不超出扩容大小(就是原容量*2), 在现在的基础上进行翻倍。

如果向切片中添加的也是个切片 格式如下:(注意要加…)

newSlice = append(oldSlice,array/slice [n:]…)

s1 := make([]int, 0)
fmt.Println(s1)  // []
fmt.Println(len(s1),cap(s1))    // 0 0
// 一次 添加多个值,且添加后的长度大于扩容一次的大小,会导致容量和长度相等.
s1 = append(s1,2,3,4)
fmt.Println(s1) // [2,3,4]
fmt.Println(len(s1),cap(s1))  //3 3
// 等到下次添加内容时如果不超出扩容大小(就是原容量*2), 在现在的基础上进行翻倍。
s1 = append(s1,5)
fmt.Println(s1)  //[2 3 4 5]
fmt.Println(len(s1),cap(s1))  //4 6
// 向切片中添加的也是个切片
a:=make([]string,0)
b:=make([]string,1,3)
b = append(b, "伤病","大傻逼")
a = append(b,b[1:]...)
fmt.Println(a) // [ 伤病 大傻逼 伤病 大傻逼]

通过数组产生切片

定义数组后,取出数组中一个片段,这个片段就是切片类型。

切片是指针,指向数组元素地址,修改切片的内容数组的内容会跟随变化。

当切片内容在增加时

如果增加后切片的长度没有超出数组,修改切片也是在修改数组(即和原数组指向同一个地址)

如果增加后切片的长度超出数组,会重新开辟一块空间放切片的内容

slice := [] int {1,23,4,5}
// 1.如果增加后切片的长度没有超出数组,修改切片也是在修改数组(即和原数组指向同一个地址)
sb := [...]int{1,2,3,4,5}
s :=sb[0:]
s[0]=3
fmt.Println(sb) // [3 2 3 4 5]
// 2. 如果增加后切片的长度超出数组,会重新开辟一块空间放切片的内容
sb := [...]int{1,2,3,4,5}
s :=sb[0:]
fmt.Printf("%p\n",&sb) //0xc00000c2a0
s = append(s,1,3,4)
fmt.Println(sb) // [1 2 3 4 5]
fmt.Println(s) // [1 2 3 4 5 1 3 4]
fmt.Printf("%p\n",&s) // 0xc000004078

c.【切片元素的删除】

Go语言标准库中没有提供删除的函数

切片也可以取其中的一段形成子切片,利用这个特性可以实现删除效果(会导致原来的内容也随之改变 所以删除的话就不要使用内容了)

slice := [5]int {1,2,3,4,5}
n:= 2
newSlice := slice[0:n]
newSlice = append(newSlice, slice[n+1:]...)
fmt.Println(newSlice) // [1 2 4 5]
newSlice[0]=8888
// 会导致原来的内容也随之改变 所以删除的话就不要使用内容了
fmt.Println(slice) // [8888 2 4 5 5]

d.【copy函数】

通过copy函数可以把一个切片内容复制到另一个切片中

 func copy(dst, src []Type) int

Go语言标准库源码定义如下

第一个参数是 目标切片 接收第二个参数内容

第二个参数是源切片,把内容拷贝到第一个参数中

copy时严格按照角标进行

使用cope函数去实现删除功能(这个方法可以保证原切片内容不变)

g:=[]int{1,2,3,4,5,6}
n := 2 //要删除元素的索引
newSlice := make([]int,n)
copy(newSlice,g[0:n])
newSlice = append(newSlice,g[n+1:]...)
fmt.Println(g) // [1 2 3 4 5 6]
fmt.Println(newSlice) // [1 2 4 5 6]

e.【排序】

// 1. 正常冒泡排序
func main() {
  arr:=[]int{1,234,5,1,52,2,4}
  BubbleSort(arr)
}
func BubbleSort(arr[]int)  {
  for i := 0; i < len(arr); i++ {
    for i1 := 0; i1 < len(arr)-i-1; i1++ {
      if arr[i1]<arr[i1+1] {
        continue
      }else {
        arr[i1],arr[i1+1] = arr[i1+1],arr[i1]
      }
    }
  }
  fmt.Println(arr) // [1 1 2 4 5 52 234]
}

f.【切片作为参数传递】

// 切片作为参数传递为引用传递,函数对切片的操作,同样也作用与原切片。
// 切片本身就是个引用变量 是一个指向某片内存的地址
func modify(array []int) {
  fmt.Println(array) // [1 2 3 4 5]
  array[0] = 10
  fmt.Println(array) // [10 2 3 4 5]
}
func main() {
  array := []int{1, 2, 3, 4, 5}
  modify(array)
  fmt.Println(array) // [10 2 3 4 5]
}

注意:使用append添加元素,容量足够,则在原基础之上添加数据,地址不会发生改变,容量如果不够,就会重新开辟一片空间。

a:=[]int{1,3,4}
s:=a
s = append(s, 1,2,3,4,5,6)
fmt.Printf("%p\n",a) // 0xc000012168
fmt.Println(a) // [1 3 4]
fmt.Printf("%p\n",s) // 0xc0000160f0
fmt.Println(s) // [1 3 4 1 2 3 4 5 6]
//-------------------------------------------
b:=make([]int,0,3)
b = append(b, 1,2,3)
s:=b
s[0]=888
fmt.Printf("%p\n",b) // 0xc000012168
fmt.Println(b) // [888 2 3]
fmt.Printf("%p\n",s) // 0xc000012168
fmt.Println(s) // [888 2 3]


相关文章
|
14天前
|
运维 监控 算法
监控局域网其他电脑:Go 语言迪杰斯特拉算法的高效应用
在信息化时代,监控局域网成为网络管理与安全防护的关键需求。本文探讨了迪杰斯特拉(Dijkstra)算法在监控局域网中的应用,通过计算最短路径优化数据传输和故障检测。文中提供了使用Go语言实现的代码例程,展示了如何高效地进行网络监控,确保局域网的稳定运行和数据安全。迪杰斯特拉算法能减少传输延迟和带宽消耗,及时发现并处理网络故障,适用于复杂网络环境下的管理和维护。
|
15天前
|
编译器 Go
揭秘 Go 语言中空结构体的强大用法
Go 语言中的空结构体 `struct{}` 不包含任何字段,不占用内存空间。它在实际编程中有多种典型用法:1) 结合 map 实现集合(set)类型;2) 与 channel 搭配用于信号通知;3) 申请超大容量的 Slice 和 Array 以节省内存;4) 作为接口实现时明确表示不关注值。此外,需要注意的是,空结构体作为字段时可能会因内存对齐原因占用额外空间。建议将空结构体放在外层结构体的第一个字段以优化内存使用。
|
15天前
|
存储 缓存 安全
Go 语言中的 Sync.Map 详解:并发安全的 Map 实现
`sync.Map` 是 Go 语言中用于并发安全操作的 Map 实现,适用于读多写少的场景。它通过两个底层 Map(`read` 和 `dirty`)实现读写分离,提供高效的读性能。主要方法包括 `Store`、`Load`、`Delete` 等。在大量写入时性能可能下降,需谨慎选择使用场景。
|
15天前
|
存储 缓存 监控
企业监控软件中 Go 语言哈希表算法的应用研究与分析
在数字化时代,企业监控软件对企业的稳定运营至关重要。哈希表(散列表)作为高效的数据结构,广泛应用于企业监控中,如设备状态管理、数据分类和缓存机制。Go 语言中的 map 实现了哈希表,能快速处理海量监控数据,确保实时准确反映设备状态,提升系统性能,助力企业实现智能化管理。
29 3
|
16天前
|
SQL 安全 Java
阿里双十一背后的Go语言实践:百万QPS网关的设计与实现
解析阿里核心网关如何利用Go协程池、RingBuffer、零拷贝技术支撑亿级流量。 重点分享: ① 如何用gRPC拦截器实现熔断限流; ② Sync.Map在高并发读写中的取舍。
|
17天前
|
存储 算法 安全
基于 Go 语言的公司内网管理软件哈希表算法深度解析与研究
在数字化办公中,公司内网管理软件通过哈希表算法保障信息安全与高效管理。哈希表基于键值对存储和查找,如用户登录验证、设备信息管理和文件权限控制等场景,Go语言实现的哈希表能快速验证用户信息,提升管理效率,确保网络稳定运行。
27 0
|
19天前
|
开发框架 前端开发 Go
eino — 基于go语言的大模型应用开发框架(二)
本文介绍了如何使用Eino框架实现一个基本的LLM(大语言模型)应用。Eino中的`ChatModel`接口提供了与不同大模型服务(如OpenAI、Ollama等)交互的统一方式,支持生成完整响应、流式响应和绑定工具等功能。`Generate`方法用于生成完整的模型响应,`Stream`方法以流式方式返回结果,`BindTools`方法为模型绑定工具。此外,还介绍了通过`Option`模式配置模型参数及模板功能,支持基于前端和用户自定义的角色及Prompt。目前主要聚焦于`ChatModel`的`Generate`方法,后续将继续深入学习。
163 7
|
19天前
|
存储 开发框架 Devops
eino — 基于go语言的大模型应用开发框架(一)
Eino 是一个受开源社区优秀LLM应用开发框架(如LangChain和LlamaIndex)启发的Go语言框架,强调简洁性、可扩展性和可靠性。它提供了易于复用的组件、强大的编排框架、简洁明了的API、最佳实践集合及实用的DevOps工具,支持快速构建和部署LLM应用。Eino不仅兼容多种模型库(如OpenAI、Ollama、Ark),还提供详细的官方文档和活跃的社区支持,便于开发者上手使用。
124 8
|
20天前
|
存储 Go
Go 语言入门指南:切片
Golang中的切片(Slice)是基于数组的动态序列,支持变长操作。它由指针、长度和容量三部分组成,底层引用一个连续的数组片段。切片提供灵活的增减元素功能,语法形式为`[]T`,其中T为元素类型。相比固定长度的数组,切片更常用,允许动态调整大小,并且多个切片可以共享同一底层数组。通过内置的`make`函数可创建指定长度和容量的切片。需要注意的是,切片不能直接比较,只能与`nil`比较,且空切片的长度为0。
Go 语言入门指南:切片
|
20天前
|
存储 算法 Go
Go语言实战:错误处理和panic_recover之自定义错误类型
本文深入探讨了Go语言中的错误处理和panic/recover机制,涵盖错误处理的基本概念、自定义错误类型的定义、panic和recover的工作原理及应用场景。通过具体代码示例介绍了如何定义自定义错误类型、检查和处理错误值,并使用panic和recover处理运行时错误。文章还讨论了错误处理在实际开发中的应用,如网络编程、文件操作和并发编程,并推荐了一些学习资源。最后展望了未来Go语言在错误处理方面的优化方向。