Go语言自学笔记(三)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_18800771/article/details/97130292

Go语言中的复合类型:

指针:每个变量都有两层数据,一是变量的内存,二是变量的地址即内存的标签。

我们通过正常输的都是变量的内存:

var a int = 1
fmt.Println(a)

如果我们需要操作指针,则需要对地址操作,也就是内存的标签:&a(格式化输出用%p占位)

fmt.Println(&a)

保存int变量的地址需要指针类型*int,若保存*int类型地址则需要**int类型指针。

var p *int = &a    //指针变量指向谁就把谁的地址赋值给指针变量,p是*int类型指向int类型变量
*p = 10    //为指针所指向的变量赋值

需要注意的是:*p操作的不是p的内存,而是p所指向的内存,等价于对a操作。

Go语言保留了指针,但与其他变成语言不同的是:

1.指针的默认值是nil,没有NULL常量。

2.操作符“&”取变量地址,“*”通过指针访问目标对象。

3.不支持指针运算,不支持“->”运算符,直接用“.”访问目标成员。

不要操作没有合法指向的内存:未初始化或赋值(nil)的指针,否则会报错。

为了解决此问题,我们可以用new()函数来生成空间用于操作内存,类似于动态分配空间,但Go语言不需要考虑释放空间,其有自己的内存回收,不用关心生命周期或是怎样删除。

p=new(int)        //赋值
q:=new(int)        //自动推导

指针与函数配合使用:

普通变量作为函数参数:通过函数交换变量的值

func swap(a,b int){
    a,b=b,a
}
func mian(){
    a,b:=10,20
    swap(a,b)
}

通过此方法,由于变量作用域不同,定义不同,出现结果:自定义函数内部变量值发生了交换,进行了值得传递,但在主函数之中并没有交换变量的值。

地址传递:

func swap(p1,p2 *int){
    *p1,*p2=*p2,*p1
}
func mian(){
    a,b:=10,20
    swap(&a,&b)
}

通过此方法,从恩身上交换了两个变量的内存,使主函数之中实现了变量值得交换。

Go语言中的数组:简化同类型声明并初始化的操作,是同一个类型的集合。

数组的定义:

var arr [50]int        //50代表数组的容量,下标从0-len()-1

需要注意的是:

1.数组的元素个数必须是常量。

2.通过下标操作数组从0到len()-1。

数组的手动赋值和打印:

使用for循环赋值并打印:

for i:=0;i<len(arr);i++{
    arr[i]=i
    fmt.Printf("arr[%d]=%d",i,arr[i])
}

也可以使用迭代打印:

for i,data:=range arr{
    fmt.Printf("arr[%d]=%d",i,data)
}

也可以使用直接打印:

fmt.Println(arr)

单一数组元素的赋值:

arr[10]=1        // 10是下标,这时的下标可以使用变量或者常量

数组的初始化:声明的同时赋值

全部初始化:

var arr [5]int=[5]int{1,2,3,4,5}

部分初始化:

arr:=[5]int{1,2,3}    //推荐使用自动推导类型,没有初始化的元素自动赋值为0

指定元素初始化:

arr:=[5]int{2:10,5:20}

二维数组:有多少方括号就是几维数组,就用多少层循环去操作。

二维数组的赋值和打印:

for循环赋值并打印:

var arr [3][4]int
k:=0
for i:=0;i<3;i++{
    for j:=0;j<4;j++{
        a[i][j]=k++
        fmt.Printf("a[%d][%d]=%d\n",i,j,a[i][j])
    }   
}

直接打印:

fmt.Println(arr)

自动推导及其初始化:类似于数组的嵌套,每个横坐标都是一个新的数组。

arr:=[2][2]int{{1,2},{3,4}}

也可以部分初始化:

arr:=[2][2]int{1:{1,2}}

数组的比较和赋值:比较只能用==或者!=,返回布尔变量。只能比较同样的数组类型是不是每个元素都一样。同时,同类型的数组也可以进行赋值,如arr1=arr2。

随机数的使用:需要设置种子并产生随机数。

package main
import "math/rand"
import "fmt"
func mian(){
    rand.Seed(123)    //设置种子为123
    fmt.Println(rand.Int())    //产生随机数
}

但需要注意的是:如果种子参数一样则运行程序产生的随机数都一样。

时间种子:以本机时间作为种子。

package main
import "math/rand"
import "time"
func mian(){
    rand.Seed(time.Now().UnixNano())    //设置时间种子,以当前系统时间作为种子参数
 }

我们可以再一定的范围内产生随机数:

rand.Intn(100)+0        //在0-100范围内产生随机数

冒泡排序原理:相邻两元素比较排序,最多进行n-1行排序。

package main

import "fmt"
import "math/rand"
import "time"

func main() {
	rand.Seed(time.Now().UnixNano()) //设置时间种子
	var a [10]int
	n := len(a)
	for i := 0; i < n; i++ {
		a[i] = rand.Intn(100) //生成100以内随机数
	}
	fmt.Println(a)
	//冒泡排序,相邻两元素比较排序,升序
	var b bool = true		//增加判断器以优化代码效率
	for i := 0; b == true && i < n-1; i++ {
		b = false
		for j := 0; j < n-1-i; j++ {
			if a[j] > a[j+1] {
				b = true
				a[j], a[j+1] = a[j+1], a[j]
			}
		}
	}
	fmt.Println("排序完成\n", a)
}

数组做函数参数:数组做函数参数是一种值传递,实参数组的每个元素被形参数组复制。

func modify(arr [5]int){        //形参的数组是由实参数组复制来的
    arr[0]=20
    fmt.Println(arr)
}
func main(){
    arr:=[5]int{1,2,3,4,5}
    modify(arr)
}

数组指针做函数参数:地址传递,数组指针p指向数组,*p则是所指向的内存即实参数组arr。

func modify(p *[5]int){
    (*p)[0]=20
    fmt.Println(*p)
}
func main(){
    arr:=[5]int{1,2,3,4,5}
    modify(&arr)
}

数组的大小是固定的,在参数传递时必须完整传递,故引入切片的概念和功能来弥补数组的缺点。同时切片也可以理解为变长的动态数组。切片不指定长度,从数组切入。

切片的初始化:

a:=[...]int{1,2,3,0,0}    //中括号内可以省略...

切片和数组的区别:

1.数组[]长度是固定的一个常量,不能修改长度,长度和容量都是那个常量。

2.切片[]为空或...,切片的长度和容量可以不固定。

3.append(s,11) 可为切片s末尾追加元素11。

切片的创建:

自动推导同时初始化:

s1:=[]int{1,2,3,4,5}

借助make函数初始化:格式:make(切片类型,长度,容量)

s2:=make([]int, 5, 10)        //切片容量可以不指定,默认容量与长度相同

切片的截取:根本格式:s:=a[low:high:max]

s:=a[0:3:5]

內建函数:len(s)打印切片长度,cap(s)打印切片容量。

low:切片下标的起点

high:切片下标的终点(不包括)

切片的长度len=high-low

切片的容量cap=max-low

单独操作切片内的某个元素:与操作数组的方式相同

data:=array[1]

 切片在数组上的截取:

array:=[]int{1,2,3,4,5,6,7,8,9}
s1:=array[:]    //[0:len(array):len(array)]不指定,默认容量与长度相同
s2:=array[3:6:7]    //从array[3]开始到array[5],长度是3,容量是4
s3:=array[:6]    //从0开始取6个元素,长度和容量都是6,此方法最为常用
s4:=array[3:]    //从array[3]开始,取到结尾
s5:=array[2:5]    //从array[2]开始,取3个元素

需要注意的是:切片指向底层数组,在对某个切片元素重新赋值时,无论切片经过多少次重切,切片所指向的最底层数组的相应元素也会跟着被重新赋值,改变。

切片的append追加函数:自动扩容,一旦超过原底层容量,容量通常以两倍的容量重新分配底层数组并复制数据。

append(s,1)    //在切片s末尾添加元素1

切片的copy函数:对某一切片的前几位用原切片做覆盖。copy(目的切片,原切片)

copy(s1,s2)    //将s2替换到s1的前几位

切片做函数参数:引用传递,传递方式与数组值传递相同,但不会完整传递,只会传递所需的某个元素。

产生随机数:产生四位随机数。

传统方法:

rand.Intn(10000)    //产生随机数通过if判断是否大于等于1000

推荐方法:

rand.Intn(9000)+1000    //1000是四位数最小值,9000+1000是四位数最大值(不包含)

切片保存数字的每一位数(4位):可以通过数字的取商‘/’与取余‘%’配合使用,取出数字每一位并追加给切片或给切片元素赋值,如:

s[0]=2514/1000    //求千位
s[1]=(2514%1000)/100    //求百位

Go语言中的map:键值对类型key-value 格式:map[key类型]vale类型{}

需要注意的是:

1.map的key-value是对应的,key不能出现重复。

2.map的打印和返回时无序的。

3.切片与函数不能作为key值。

4.几乎任何类型都可以作为value值。

5.map只有长度没有容量。

map的定义:

直接定义:

var m1 map[int]string        //key为int,value为string

通过make函数定义:

m2:=make(map[int]string)
m3:=make(map[int]string,10)    //指定长度:只是预先分配空间,但没有存数据,长度仍是0

map的初始化:

m:=map[int]string{1:”mike”,2:”tom”,3:”jack”}	//常用,键值一定是唯一的

map的赋值:

m[1]=”go”    //为已经存在的key值赋值--修改value
m[5]=”stop”    //新增key,下标超出分配空间,会自动扩充长度:不存在key值则追加,map底层自动扩容

map的遍历:

for key,value:=range m{}    //遍历结果是无序的

判断key是否存在:

value,info:=m[1]     //info返回布尔类型用来判断key:1是否存在

删除key键值:

delete(m,1)    //删除m中key:1

map做函数参数声明及调用:引用传递,地址传递。

func fun(m map[int]string){
}
func main(){
    fun(m)
}

Go语言的结构体类型:

结构体的定义:与自定义函数同级,定义在主函数之外。

type Student struct{
    id int
    name string
    sex byte
    age int
    addr string
}

声明结构体变量:

var s1 Student = Student{1,"tom",'M',18,"北京"}    //顺序初始化,每一个成员都要初始化
s2: = Student{name:"jack",age:20}    //部分指定成员初始化,没有初始化成员自动赋值为0

结构体指针变量初始化:声明时加“&”,使用/打印时加“*”但*可有可无。打印可以使用可以直接打印。

var p1 *Student=&Student{1,"tom",'M',18,"北京"} 
p2:=&Student{name:"jack",age:20}

结构体成员的使用:操作程序需要用到 . 运算符。

普通变量:

s1.id=10086
s1.name="zx"
s2.age=18
s2.addr="天津"

指针变量:指针有合法指向后才能操作成员。

var s Student        //先定义普通结构体变量
var p *Student        //再定义指针变量指向s保存其地址
p=&s

通过指针操作成员变量时:以下两种语句完全等价,与其他语言不同,都只能运用 . 运算符。

p.id        //推荐写法
(*p).id

new函数创建空间:

p:=new(Student)

Go语言中结构体的比较:与数组比较相类似,只能用==和!=运算符,返回布尔类型。

结构体赋值:同类型的结构体可以相互赋值。

结构体作为函数参数的使用:

值传递:同名不同作用域的函数声明以及调用。

func fun (s Student){
}
func main(){
    fun(s)        //值传递,实参不会跟着实参一起改变
}

地址传递/引用传递:

func fun(p *Student){
}
func main(){
    fun(&s)        //引用传递,参数跟着函数内部改变
}    

可见性规则:

如果想使用其他包的函数、结构体类型、结构体成员。函数名,类型名,结构体成员变量名的首字母必须大写。如果首字母是小写,则只能在同一个包里面使用。

猜你喜欢

转载自blog.csdn.net/qq_18800771/article/details/97130292