GO语言基础(三)复合类型(指针、数组、切片、map、结构体)

1.指针

【1】指针

指针是一个代表着某个内存地址的值。这个内存地址往往是在内存中存储的某一个变量的值的起始位置。
Go语言对指针的支持介于java和C/C++语言之间

基本操作:

  • 默认值为nil,没有NULL常量
  • 操作符"&"取地址,"*"通过指针访问目标对象
  • 不支持指针运算,不支持"->"运算符,直接用"."访问目标成员
package main

import (
	"fmt"
)

func main() {
	var i int = 10
        var p *int  //p为int类型的指针
	p = &i      //p指向i的地址
	fmt.Printf("i的值为%d,地址为%v,取地址后的值为%v\n", i, &i, *p)
}

【2】new函数的使用

表达式new(T)将构建一个T类型的匿名变量,所做的是为T类型的新值分配并清零一块内存空间,然后返回这块内存的地址,返回的指针类型为*T。

package main

import (
	"fmt"
)

func main() {
	p := new(int)
	*p = 666
	fmt.Printf("*p的值为%d,地址为%v\n", *p, p)
}

【3】指针用作函数参数

package main

import (
	"fmt"
)

//变换两个变量的值
func swap(a, b *int) {   //传入a,b地址指针
	*a, *b = *b, *a      //更改指针地址内存空间的值
	fmt.Printf("swap: a=%d,b=%d\n", *a, *b)
}
func main() {
	a, b := 10, 20
	swap(&a, &b)  //调用函数调换;两个变量的值
	fmt.Printf("main: a=%d,b=%d\n", a, b)
}

输出:

2.数组

【1】数组的基本操作

package main

import (
	"fmt"
)

func main() {
	var temp [20]int                 //定义数组temp,数组定义时[]内必须是常量
	for i := 0; i < len(temp); i++ { //遍历数组
		temp[i] = i + 1
		fmt.Printf("temp[%d]=%d\n", i, temp[i])
	}
}

输出:

【2】数组的初始化

package main

import (
	"fmt"
)

func main() {
	//1.全部初始化
	var a [5]int = [5]int{1, 2, 3, 4, 5}
	fmt.Println(a)
	b := [5]int{1, 2, 3, 4, 5}
	fmt.Println(b)

	//2.部分初始化
	c := [5]int{1, 2, 3}
	fmt.Println(c)

	//3.指定某个元素初始化
	d := [5]int{2: 10, 4: 20}
	fmt.Println(d)
}

输出:

【3】二维数组

package main

import (
	"fmt"
)

func main() {
	//有多少个[]就是几维
	var a [3][4]int

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

	//1.全部初始化
	b := [3][4]int{{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}}
	fmt.Println("b=", b)

	//2.部分初始化
	c := [3][4]int{{1, 2, 3, 4}, {5, 6, 7}}
	fmt.Println("c=", c)

	//3.指定位置初始化
	d := [3][4]int{1: {5, 6, 7, 8}}
	fmt.Println("d=", d)
}

输出:

【4】数组的比较和赋值

package main

import (
	"fmt"
)

func main() {
	//支持比较,只支持==或!=,比较是不是每一个元素都一样,两个数组比较数组类型要一样
	a := [5]int{1, 2, 3, 4, 5}
	b := [5]int{1, 2, 3, 4, 5}
	c := [5]int{1, 2, 3}

	fmt.Println("a==b:", a == b)
	fmt.Println("b==c:", b == c)

	//同类型的数组可以赋值
	d := [5]int{}
	d = a
	fmt.Println("d=", d)
}

输出:

【5】随机数的生成

package main

import (
	"fmt"
	"math/rand"
	"time"
)

func main() {
	//设置种子
	//如果种子参数一样,每次运行程序产生的随机数都是一样
	rand.Seed(time.Now().UnixNano()) //种子随时间变
	//rand.Seed(666) //固定种子
	//产生随机数
	for i := 0; i < 5; i++ {
		fmt.Println("rand=", rand.Int())     //随机很大的数
		fmt.Println("rand=", rand.Intn(100)) //限制在100以内的数
	}
}

输出:

【6】数组作为函数参数

package main

import (
	"fmt"
)

//数组做函数参数,他是值传递
//是参数组的每一个元素给形参数组拷贝一份
//形参的数组是实参数组的复制品
func modify(a [5]int) {
	a[0] = 666
	fmt.Println("modify a= ", a)
}

func main() {
	a := [5]int{1, 2, 3, 4, 5} //初始化

	modify(a) //数组传递过去
	fmt.Println("main: a= ", a)
}

输出:

【7】数组指针做函数参数

package main

import (
	"fmt"
)
//p指向实参数组a,是数组指针
func modify(p *[5]int) {
	(*p)[0] = 666 //指针赋值
	fmt.Println("modify a= ", *p)
}

func main() {
	a := [5]int{1, 2, 3, 4, 5} //初始化

	modify(&a) //数组地址传递过去
	fmt.Println("main: a= ", a)
}

输出:

3.切片

【1】概述

数组的长度在定义之后就不能改变,数值是值类型,每次传递都将产生一个副本。为了解决这个问题,Go语言中通过切片(slice)来弥补数组的不足,切片并不是数组或者数组指针,它通过内部指针和相关属性引用数组片段,以实现变长方案。

【2】切片的长度和容量

package main

import (
	"fmt"
)

func main() {
	a := []int{1, 2, 3, 4, 0, 0}  //初始化切片,切片[]里面为空或者...
	s := a[0:3:5]                 //取切片
	fmt.Printf("s=%v,len(s)=%d,cap(s)=%d\n", s, len(s), cap(s))
}

输出:

【3】切片的创建方式

package main

import (
	"fmt"
)

func main() {
	//传统的创建方式,自动推导类型,同时初始化
	s1 := []int{1, 2, 3, 4}
	fmt.Println("s1=", s1, "  len=", len(s1), "  cap=", cap(s1))

	//借助make函数,格式:make(切片类型,长度,容量)
	s2 := make([]int, 5, 10)
	fmt.Println("len=", len(s2), "    cap=", cap(s2))

}

输出:

【4】切片的截取

切片操作
语法含义
s[n]切片s中索引位置为n的项
s[n:m]从切片s的索引位置n到m-1处所获得的切片
s[n:]从切片s的索引位置n到len(s)-1处所获得的切片
s[:m]从切片s的索引位置0到m-1处所获得的切片
s[:]从切片s的索引位置0到len(s)-1处所获得的切片
cap(s)切片s的容量,总是>=len(s)
len(s)切片s中包含项的个数,总是<=cap(s)
s[:cap(s)]增加切片s的长度到其容量

【5】内建函数

  • append

append函数向slice切片后面添加元素,返回新的slice,会智能的增长底层数组的容量,通常以两倍容量重新分配底层数组,并复制原来的数据。

package main

import (
	"fmt"
)

func main() {
	s1 := []int{1, 2, 3}
	//在原切片的末尾添加元素
	s2 := append(s1, 4, 5)
	fmt.Println("s2=", s2)
	//如果超过原来的容量,通常以两倍容量扩容
	s := make([]int, 0, 1)
	oldCap := cap(s)
	for i := 0; i < 8; i++ {
		s = append(s, i)
		if newCap := cap(s); oldCap < newCap {
			fmt.Printf("cap: %d ===> %d\n", oldCap, newCap)
			oldCap = newCap
		}
	}
}

输出:

  • copy函数

在两个slice之间赋值数据赋值长度以len小的为基准,连个slice可以同时指向同一底层数组。

package main

import (
	"fmt"
)

func main() {
	dstslice := []int{1, 2}
	srcslice := []int{6, 6, 6, 6, 6}

	copy(dstslice, srcslice)
	fmt.Printf("dst = %d\n", dstslice)
}

输出:

【6】切片做函数参数

package main

import (
	"fmt"
	"math/rand"
	"time"
)

func InitData(s []int) {
	//设置种子
	rand.Seed(time.Now().UnixNano())
	for i := 0; i < len(s); i++ {
		s[i] = rand.Intn(100) //100以内随机数
	}
}

func Bubblesort(s []int) {
	n := len(s)
	for i := 0; i < n-1; i++ {
		for j := 0; j < n-i-1; j++ {
			if s[j] > s[j+1] {
				s[j], s[j+1] = s[j+1], s[j]
			}
		}
	}

}

func main() {
	n := 10
	s := make([]int, n)
	InitData(s) //初始化数组
	fmt.Println("排序前:", s)

	Bubblesort(s) //冒泡排序
	fmt.Println("排序后:", s)
}

输出:

4.map

【1】map的初始化和赋值

map是一组无序的key-value键值对集合,切片、函数以及包含切片的结构类型,不能作为map的键。

package main

import (
	"fmt"
)

func main() {
	//定义一个变量,类型为map[int]string
	var dict map[int]string
	//对于map只有len,没有容量
	fmt.Printf("dict = %v,len = %v\n", dict, len(dict))

	//可以通过make创建
	m1 := make(map[int]string)
	fmt.Printf("m1 = %v,len = %v\n", m1, len(m1))

	//可以通过make创建并指定长度,只是指定了容量,但是里面并没有数据,故长度为0
	m2 := make(map[int]string, 10)
	m2[1] = "mike" //元素操作,赋值,如果key值已经存在,则会更改value
	m2[2] = "go"   //如果不存在key,则增加键值对。
	m2[3] = "c++"
	fmt.Printf("m2 = %v,len = %v\n", m2, len(m2))

	//初始化,键值是唯一的
	m3 := map[int]string{1: "mike", 2: "go", 3: "c++"}
	fmt.Println("m3=", m3)
}

输出:

【2】遍历map以及如何判断一个键是否存在

package main

import (
	"fmt"
)

func main() {
	m := map[int]string{1: "mike", 2: "yoyo", 3: "go"}

	//第一个返回值为key,第二个返回值为value,遍历结果是无序的
	for key, value := range m {
		fmt.Printf("m[%d]=%s\n", key, value)
	}

	//如何判断一个key是否存在
	//第一个返回值为key所对应的value,第二个返回值为key是否存在的条件,存在ok为true
	value, ok := m[1]
	if ok == true {
		fmt.Printf("存在,value=%s\n", value)
	} else {
		fmt.Printf("不存在\n")
	}
}

输出:

【3】map删除

delete(map,  key) //map表示要删除元素的map,key为要删除的键值对的key值。

 

【4】map做函数参数

进行的是值传递,函数中所进行的操作会影响原来map.

package main

import (
	"fmt"
)

func test(m map[int]string) {
	delete(m, 1) //删除键值为1的键值对
}

func main() {
	m := map[int]string{1: "mike", 2: "yoyo", 3: "go"}
	fmt.Println(m)
	test(m)
	fmt.Println(m)
}

输出:

5.结构体

【1】概述

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

type Student struct { //学生信息结构体表示方法
	id   int
	name string
	sex  byte
	age  int
	addr string
}

【2】结构体初始化

package main

import (
	"fmt"
)

type Student struct { //学生信息结构体表示方法
	id   int
	name string
	sex  byte //字符类型
	age  int
	addr string
}

func main() {
	//顺序初始化,每个成员都要初始化
	var s1 Student = Student{1, "wang", 'm', 12, "西安"}
	fmt.Println(s1)

	//指定成员初始化,没有初始化的成员自动赋值0
	s2 := Student{name: "mike", addr: "bj"}
	fmt.Println(s2)
}

输出:

【3】结构体指针变量初始化

package main

import (
	"fmt"
)

type Student struct { //学生信息结构体表示方法
	id   int
	name string
	sex  byte //字符类型
	age  int
	addr string
}

func main() {
	//顺序初始化,每个成员都要初始化
	var s1 *Student = &Student{1, "wang", 'm', 12, "西安"}
	fmt.Println(*s1)

	//指定成员初始化,没有初始化的成员自动赋值0
	s2 := &Student{name: "mike", addr: "bj"}
	fmt.Printf("s2 type is %T\n", s2)
	fmt.Println(*s2)
}

输出:

【4】结构体不同成员的使用--普通变量

package main

import (
	"fmt"
)

type Student struct { //学生信息结构体表示方法
	id   int
	name string
	sex  byte //字符类型
	age  int
	addr string
}

func main() {
	var  s  Student
	//操作成员,使用.操作符
	s.id = 1
	s.name = "wang"
	s.addr = "bj"
	s.sex = 'm'
	fmt.Println(s)

}

输出:

【5】结构体不同成员的使用--指针变量

package main

import (
	"fmt"
)

type Student struct { //学生信息结构体表示方法
	id   int
	name string
	sex  byte //字符类型
	age  int
	addr string
}

func main() {
	//指针有合法指向后才操作成员
	//先定义一个普通结构体变量
	var s Student
	//再定义一个指针变量,保存s的地址
	var p1 *Student
	p1 = &s

	//p1.id和(*p1).id完全等价
	p1.id = 1
	p1.name = "wang"
	p1.addr = "bj"
	p1.sex = 'm'
	fmt.Println("p1=", p1)

	//方法二,通过new申请一个结构体
	p2 := new(Student)
	p2.id = 1
	p2.name = "wang"
	p2.addr = "bj"
	p2.sex = 'm'
	fmt.Println("p2=", p2)

}

输出:

【6】结构体的比较

俩个结构体可以使用==和!=进行比较

【7】结构体作为函数参数--值传递

package main

import (
	"fmt"
)

type Student struct { //学生信息结构体表示方法
	id   int
	name string
	sex  byte //字符类型
	age  int
	addr string
}

func test01(s Student) {
	s.id = 666
	fmt.Println("test01=", s)
}

func main() {
	s1 := Student{1, "wang", 'm', 12, "bj"}
	test01(s1) //值传递,形参无法改实参
	fmt.Println("main=", s1)
}

输出:

【8】结构体作为函数参数--地址传递

package main

import (
	"fmt"
)

type Student struct { //学生信息结构体表示方法
	id   int
	name string
	sex  byte //字符类型
	age  int
	addr string
}

func test01(p *Student) {
	p.id = 666
}

func main() {
	s1 := Student{1, "wang", 'm', 12, "bj"}
	test01(&s1) //地址传递(引用传递),形参可以改实参
	fmt.Println("main=", s1)
}

输出:

附加:
如果想使用别的包的函数、结构体、结构体成员,函数名、结构体名、结构体成员名首字母必须大写,如果是小写只能在同一个包内使用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值