前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Go语言——复合类型

Go语言——复合类型

作者头像
传说之下的花儿
发布2023-04-16 14:50:04
3800
发布2023-04-16 14:50:04
举报

1. 数组

a.【声明数组】

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

b.【初始化数组】

代码语言:javascript
复制
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.【数组作为参数传递】

代码语言:javascript
复制
// 正常情况下 数组为值传递 即传递的是数组的值 在函数中对数组的操作 对于原数组无效。
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.【冒泡排序】

代码语言:javascript
复制
/*
	数组练习:冒泡排序
	注意:
		数组与切片的区别
*/
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一样 声明二维数组:

代码语言:javascript
复制
var name [n] [m] 
// 使用和java一样  n为行 m为列

数组的声明与赋值:

代码语言:javascript
复制
// 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.【变量地址】

  1. 变量本质就是内存中一块数据的标记,把值存储到变量中实质是把值存储到内存中
  2. 每次对变量重新赋值就是在修改对应变量地址中的内容
  3. 重新创建一个非引用型变量(即使是把已有变量直接赋值给新变量)也会新开辟内存地址

b.【指针变量】

  1. 指针变量指向一个值的内存地址
  2. 使用&+变量 返回值就是一个指针类型
  3. 声明指针不会开辟内存地址,只是准备指向内存某个空间, 而声明变量会开辟内存地址,准备存放内容. 所以指针类型变量都是把一个变量的地址赋值给指针变量。
  4. 使用 *+指针 能够获取 和 操作 内存地址中的值 所以 *+指针 == 直接使用变量
  5. 应用指针可以实现多个地方操作同一个内存地址的值(在方法参数应用指针较多)

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

使用var变量名 *类型、 声明指针类型变量 【声明格式】 *var name type *号用于指定变量是作为一个指针。

代码语言:javascript
复制
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)

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

d.【空指针与野指针】

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

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

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

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

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

代码语言:javascript
复制
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.【指针作为参数传递】

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

代码语言:javascript
复制
func main() {
	a := new(int)
	*a = 1
	test(a)
	fmt.Println(*a)
}
func test(a *int) {
	*a = 2
}

g.【多级指针】

指针本身也是值,且这个值和 12 一样,是不可以寻址的,这也是为什么 &(&a) 不行,但是 先b = &a,再&b 却可以。

代码语言:javascript
复制
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 二级指针:指向一级指针的地址
}

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

代码语言:javascript
复制
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. 切片

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

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

a.【切片是引用类型】

  1. 引用类型在变量之间赋值时传递的是地址.引用类型变量就是这个类型的指针.切片就是引用类型。
  2. 值类型在变量之间赋值时传递的是值的副本(就是复制了值 但是内存地址是重新开辟的)

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

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

make 函数

  1. Go语言中可以使用make函数创建 slice、map、 channel、 interface
  2. 使用make函数定义 无内容,但不是nil 的切片,意味着切片已经申请了内存空间
  3. 语法格式: make(类型,初始长度[,初始容量])
  4. 初始容量可以省略,默认和长度相等
  5. 长度 表示切片中元素的实际个数, 容量 表示切片占用空间大小, 且切片成倍增加.当增加到1024后按照一定百分比增加。
  6. 可以使用len() cap() 查看长度和容量
  7. 注意 长度和容量的不同
代码语言:javascript
复制
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 函数

  1. func append(slice []Type, elems …Type) []Type slice —> slice = append(slice, elem1, elem2) 或 slice = append(slice, anotherSlice…)
  2. 可以向切片中添加一一个或多个值,添加后 必须使用切片接收append()函数返回值
  3. 如果添加一次 添加多个值,且添加后的长度大于扩容一次的大小,容量和长度相等.等到下次添加内容时如果不超出扩容大小(就是原容量*2), 在现在的基础上进行翻倍。
  4. 如果向切片中添加的也是个切片 格式如下:(注意要加…) newSlice = append(oldSlice,array/slice [n:]…)
代码语言:javascript
复制
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) // [ 伤病 大傻逼 伤病 大傻逼]

通过数组产生切片

  1. 定义数组后,取出数组中一个片段,这个片段就是切片类型。
  2. 切片是指针,指向数组元素地址,修改切片的内容数组的内容会跟随变化。
  3. 当切片内容在增加时
  4. 如果增加后切片的长度没有超出数组,修改切片也是在修改数组(即和原数组指向同一个地址)
  5. 如果增加后切片的长度超出数组,会重新开辟一块空间放切片的内容
代码语言:javascript
复制
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语言标准库中没有提供删除的函数

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

代码语言:javascript
复制
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函数】

  1. 通过copy函数可以把一个切片内容复制到另一个切片中   func copy(dst, src []Type) int
  2. Go语言标准库源码定义如下 第一个参数是 目标切片 接收第二个参数内容 第二个参数是源切片,把内容拷贝到第一个参数中
  3. copy时严格按照角标进行
  4. 使用cope函数去实现删除功能(这个方法可以保证原切片内容不变)
代码语言:javascript
复制
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.【排序】

代码语言:javascript
复制
// 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.【切片作为参数传递】

代码语言:javascript
复制
// 切片作为参数传递为引用传递,函数对切片的操作,同样也作用与原切片。
// 切片本身就是个引用变量 是一个指向某片内存的地址
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添加元素,容量足够,则在原基础之上添加数据,地址不会发生改变,容量如果不够,就会重新开辟一片空间。

代码语言:javascript
复制
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]

5. map集合

map以散列表方式存储键值对集合

map中每个元素都是键值对

声明格式: map [键的类型] 值的类型{key:value,key:value}

key是操作map的唯一标准可以通过key对map中元素进行增加/删除/修改/查看

key是唯一的,添加重 复的key会覆盖之前的元素.

map是 引用类型 (不是值类型,目前学到的引用类型就是切片和map),只声明时为空指针(nil)

map读写数据时并不是并发安全的,可以结合RWMutex保证并发安全(RWMutex在后面讲解)

代码语言:javascript
复制
fmt.Println(map01) // map[名称:单冰 年龄:123]
map01["名称"]= "傻逼"
fmt.Println(map01) // map[名称:傻逼 年龄:123]

a.【实例化map的方式】

使用make函数 ap01:= make(map[int]int)

可以在声明map时直接给map赋初始值.注意初始值在一行和在多行写时的语法区别 := map[int]int{1:2,2:3}

map中元素键值对语法满足: key:value

key和value的类型必须和map[key]value类型严格对应

代码语言:javascript
复制
map01 := make(map[int]string)
map02 := map[string]string{"年龄":"123","名称":"单冰"}

b.【操作map中的元素】

使用key判断,如果key不存在向map中新增数据,如果key存在会覆盖map中元素

Go语言标准库中提供了对map元素删除的函数,使用顶层delete()即可完成删除 如果key存在,执行删除元素 如果key不存在,map中内容不变,也不会有错误

代码语言:javascript
复制
map01 := map[int]int{1:1,2:2}
delete(map01, 1)

查看map中key对应的value 如果有 返回对应value 如果没有 返回对应类型的默认值

value,ok := s[3] 返回俩值 一个是key对应value 一个是该key是否存在

代码语言:javascript
复制
func main() {
    map01 := make(map[int]int)
    a,b:=map01[3]
    fmt.Println(a,b) // 0 false
}

c.【map作为参数传递】

同切片一样 都是引用传递,函数中对map的操作就是对原map的操作。

代码语言:javascript
复制
func main() {
	map01 := map[int]int{1:1,2:2}
	modify(map01)
	fmt.Println(map01)

}
func modify(m map[int]int)  {
	m[3] = 3
	m[4] = 4
}

6. 结构体

有时我们需要将不同类型的数据组合成一个有机的整体。

如:一个学生有学号/姓名/性别/年龄/地址等属性。

显然单独定义以上变量比较繁琐,数据不便于管理。

代码语言:javascript
复制
// 正常写法
var id int
var name string
var sex string
var age int 
var addr string
// 使用结构体表示法
type Student struct {
    id int
    name string
    sex string
    age int
    addr string
}

结构体是一种聚合的数据类型,它是由一系列具有相同类型或不同类型的数据构成的数据集合。

每个数据称为结构体的成员。

注意:结构体的定义是在主函数的外面的。

a.【结构体定义与初始化】

定义:

type 结构体名 struct {

​ (结构体成员列表) ​ 成员名 数据类型

}

定义结构体变量:

var 变量名 结构体名

代码语言:javascript
复制
type Student struct {
    id int
    name string
    sex string
    age int
    addr string
    
    function func(int2 int) string
	slice []int
    arr [4]int
    map01 map[int]int
}

注意:

  1. 结构体名 大小写问题,见可见性部分。

初始化:

代码语言:javascript
复制
//1. 顺序初始化(每个成员必须初始化 且顺序要一致)
var stu1 Student = Student{1,"xxx","男",1,"地球"}
//2. 自动推导 和指定成员赋值 未赋值的成员为默认值
stu1 := Student{1,"xxx","男",1,"地球"}
stu1 := Student{name: "xxx",id: 1}

fmt.Println(stu1.name) // xxx

b.【结构体变量的定义与赋值】

代码语言:javascript
复制
var stu1 Student
stu1.id = 1
stu1.name = "xxx"

注意:

结构体名默认指向的就是第一个成员的地址

代码语言:javascript
复制
var stu1 Student
stu1.id = 1
stu1.name = "xxx"
fmt.Printf("%p\n",&stu1) // 0xc00001e080
fmt.Printf("%p\n",&stu1.id) // 0xc00001e080
fmt.Printf("%p\n",&stu1.name) // 0xc0000dc008

类似于java中的 类 student类 可以有成员变量 也可以有成员方法 。

c.【结构体数组 与 切片 与 map】

代码语言:javascript
复制
type Student struct {
	id int
	age int
}

func main() {
	var stu01 Student
	var stu02 Student
	stu01.id=1
	stu02.id=2
	// 存放类型为结构体类型的数组、和 切片
	var stuArr = [2]Student{
		stu01,
		stu02,
	}
	fmt.Println(stuArr)
	var stuSli = []Student{
		stu01,
		stu02,
	}
	fmt.Println(stuSli)
	var stuMap  = map[int]Student{
		1:stu01,
		2:stu02,
	} 
	fmt.Println(stuMap)
}

d.【结构体作为参数传递】

结构体作为函数参数传递的时候为值传递

代码语言:javascript
复制
type Student struct {
	id int
	age int
}
func main() {
	var stu01 Student = Student{1,19}
	test001(stu01)
	fmt.Println(stu01) // {1 19}

}
func test001(student Student)  {
	student.age=333
}

e.【可见性】

Go语言对关键字的增加非常吝啬,其中没有private、 protected、 public这样的关键字。

要使某个符号对其他包(package)可见(即可以访问),需要将该符号定义为以大写字母开头。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-07-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 数组
    • a.【声明数组】
      • b.【初始化数组】
        • c.【数组作为参数传递】
          • d.【冒泡排序】
          • 2. 二维数组
          • 3. 指针
            • a.【变量地址】
              • b.【指针变量】
                • c.【指针的声明与赋值】
                  • d.【空指针与野指针】
                    • e.【指针与数组 切片 map 结构体】
                      • f.【指针作为参数传递】
                        • g.【多级指针】
                        • 4. 切片
                          • a.【切片是引用类型】
                            • b.【切片的声明与赋值】
                              • c.【切片元素的删除】
                                • d.【copy函数】
                                  • e.【排序】
                                    • f.【切片作为参数传递】
                                    • 5. map集合
                                      • a.【实例化map的方式】
                                        • b.【操作map中的元素】
                                          • c.【map作为参数传递】
                                          • 6. 结构体
                                            • a.【结构体定义与初始化】
                                              • b.【结构体变量的定义与赋值】
                                                • c.【结构体数组 与 切片 与 map】
                                                  • d.【结构体作为参数传递】
                                                    • e.【可见性】
                                                    领券
                                                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档