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
,2
一样,是不可以寻址的,这也是为什么&(&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]