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) //引用传递,参数跟着函数内部改变
}
可见性规则:
如果想使用其他包的函数、结构体类型、结构体成员。函数名,类型名,结构体成员变量名的首字母必须大写。如果首字母是小写,则只能在同一个包里面使用。