1 指针
Go语言中的函数传参都是值拷贝,当想要修改某个变量的时候,可以创建一个指向该变量地址的指针变量。传递数据使用指针,而无须拷贝数据。
类型指针不能进行偏移和运算。
Go语言中的指针操作非常简单,只需要记住两个符号:&(取地址)和*(根据地址取值)。
1.1 指针地址和指针类型
每个变量在运行时都拥有一个地址,这个地址代表变量在内存中的位置。
Go语言中使用&字符放在变量前面对变量进行取地址操作。
Go语言中的值类型(int、float、bool、string、array、struct)都有对应的指针类型,如:*int、*int64、*string等。
1.2 指针语法
一个指针变量指向了一个值的内存地址。(声明了一个指针之后,可以像变量赋值一样,把一个值的内存地址放入到指针当中。)
类似于变量和常量,在使用指针前需要声明指针。指针声明格式如下:
var var_name *var-type
参数说明:
1.var-type:指针类型
2.var_name:指针变量名
3.*:指定变量是作为一个指针。
1.3 指针的声明与使用
// 指针声明:
var ip *int /* 指向整型*/
var fp *float32 /* 指向浮点型 */
func pointer() {
var ip *int // 声明指针变量
fmt.Printf("ip变量储存的指针地址: %v\n", ip) // ip: <nil> 空指针
fmt.Printf("ip变量的类型: %T\n", ip) // *int --- int类型的指针类型
var i int = 66 // 声明实际变量
ip = &i // 指针变量的存储地址
fmt.Printf("i变量的地址: %x\n", &i)
// 指针变量的存储地址
fmt.Printf("ip变量储存的指针地址: %v\n", ip)
// 使用指针访问值
fmt.Printf("ip变量的值: %d\n", *ip)
var sp *string
var s string = "小罗"
sp = &s
fmt.Printf("s变量的地址: %x\n", &s)
fmt.Printf("sp变量储存的指针地址: %v\n", sp)
fmt.Printf("sp变量的值: %v\n", *sp)
}
2 指向数组的指针
2.1 语法
var ptr [MAX]*int; 表示数组里面元素的类型是指针类型
2.2 实例演示
const MAX int = 3
func maxArry() {
pointerArry := []string{"美国", "加拿大", "墨西哥"} // 定义字符串切片
var ptr [MAX]*string // 定义一个指针数组,每个元素都是*string类型
fmt.Printf("ptr: %v\n", ptr) // 初始化打印
// 把每个字符串的地址赋值给指针数组
var i int
for i = 0; i < MAX; i++ {
ptr[i] = &pointerArry[i]
}
fmt.Printf("ptr: %v\n", ptr)
// 遍历指针数组,打印指针指向的字符串
for i = 0; i < MAX; i++ {
fmt.Printf("pointerArry[%d] = %s\n", i, *ptr[i])
}
}
3 类型定义和类型别名
3.1 类型定义
// 类型定义的语法: type NewType Type
// 类型定义---i为MyInt类型
func defineType() {
type MyInt int
var i MyInt
i = 500
fmt.Printf("i的类型是: %T\ni的值是:%v\n", i, i)
}
3.2 类型别名
// 类型别名的语法: type NewType = Type
// 类型别名---i还是int类型
func aliasType() {
type MyInt2 = int
var i MyInt2
i = 56
fmt.Printf("i的类型是: %T\ni的值是: %v\n", i, i)
}
3.3 类型定义和类型别名的区别
1.类型定义相当于定义了一个全新的类型,与之前的类型不同;类型别名并没有定义一个新的类型,而是使用一个别名来替换之前的类型
2.类型别名只会在代码中存在,在编译完成之后并不会存在该别名
3.类型别名和原来的类型是一致的,所以原来类型所拥有的方法,类型别名中也可以调用,但是如果是重新定义的一个类型,那么不可以调用之前的任何方法
4 结构体
Go语言没有面向对象的概念了,但是可以使用结构体来实现,面向对象编程的一些特性,例如:继承、封装、组合、多态等特性。
4.1 结构体定义
结构体的定义和类型定义类似,只不过多了一个struct关键字,语法结构如下:
type struct_variable_type struct {
member definition;
member definition;
...
member definition;
}
参数说明:
1.`type`:结构体定义关键字
2.`struct_variable_type`:结构体类型名称
3.`struct`:结构体定义关键字
4.`member definition;`:成员定义
4.2 结构体实例
定义一个人的结构体Person:
type Person struct {
id int
name string
age int
email string
}
// 以上定义一个Person结构体,有四个成员,来描述一个Person的信息。
// 同类型的可以合并到一行,例如:
type Person struct {
id, age int
name, email string
}
4.3 声明结构体变量
声明一个结构体变量和声明一个普通变量相同,例如:
// 自定义了一个类型
type Person struct {
id, age int
name, email string
}
type Customer struct {
id, age int
name, email string
}
func main() {
// 声明结构体变量
var Tom Person
fmt.Printf("Tom: %v\n", Tom)
// 声明结构体变量
kite := Customer{}
fmt.Printf("kite: %v\n", kite)
}
// 运行结果: Tom: {0 0 } kite: {0 0 }
// 结构体成员,在没有赋值之前都是零值。
4.4 访问结构体成员
可使用点运算符(.),来访问结构体成员,例如:
// 自定义了一个类型
type Customer struct {
id, age int
name, email string
}
func main() {
// 声明结构体变量
kite := Customer{}
kite.id = 001
kite.name = "kite"
kite.age = 31
kite.email = "kite@163.com"
fmt.Printf("kite: %v\n", kite)
fmt.Printf("kite.email: %v\n", kite.email)
}
// 运行结果如下:kite: {1 31 kite kite@163.com} kite.email: kite@163.com
4.5 匿名结构体
如果结构体是临时使用,可以不用起名字,直接使用,例如:
func dog() {
var Dog struct {
id, age int
name, address string
}
Dog.id = 999
Dog.age = 10
Dog.name = "毛毛"
Dog.address = "陕西省渭南市临渭区xx村"
fmt.Printf("Dog: %v\n", Dog)
}
func main() {
dog()
}
4.6 结构体初始化
未初始化的结构体,成员都是零值:int—0、float—0.0、bool—false、string—nil
type Person struct {
id, age int
name, email string
}
func main() {
pite := Person{}
fmt.Printf("pite: %v\n", pite)
}
// 运行结果:pite: {0 0 "" ""}
4.6.1 键值对对结构体初始化
type kv_struct struct {
id, age int
name, email string
}
func main() {
var Tom kv_struct
Tom = kv_struct{
id: 1,
name: "Tom",
age: 20,
email: "Tom@163.com",
}
fmt.Printf("Tom: %v\n", Tom)
}
// 运行结果:Tom: {1 20 Tom Tom@163.com}
4.6.2 值的列表初始化
type vlist_struct struct {
id, age int
name, email string
}
func main() {
John := vlist_struct{
1,
31,
"John",
"John@163.com",
}
fmt.Printf("John: %v\n", John)
}
// 运行结果:John: {1 31 John John@163.com}
注意:
- 必须初始化结构体的所有字段。
- 初始值的填充顺序必须与字段在结构体中的声明顺序一致。
- 该方式不能和键值初始化方式混用。
4.6.3 部分成员初始化
用不到的成员,可以不进行初始化
type member_struct struct {
id, age int
name, email string
}
func main() {
Dave := member_struct{
id: 1,
name: "Dave",
}
fmt.Printf("Dave: %v\n", Dave)
}
// 运行结果:Dave: {1 0 Dave }
4.7 结构体指针
4.7.1 普通变量的指针
func variable_pointer() {
var name string
name = "pite"
fmt.Printf("name变量的值: %v\n", name)
fmt.Printf("name变量的地址: %x\n", &name)
var p_name *string // p_name 指针类型
p_name = &name // &name 取name地址
// 输出指针地址
fmt.Printf("p_name变量储存的指针地址: %v\n", p_name)
fmt.Printf("p_name变量的类型: %T\n", p_name)
// 输出指针指向的内容值
fmt.Printf("*p_name变量的值: %v\n", *p_name)
}
// 运行结果:name变量的值: pite name变量的地址: 35edf2708030 p_name变量储存的指针地址: 0x35edf2708030
// p_name变量的类型: *string *p_name变量的值: pite
4.7.2 Go的结构体指针
func struct_pointer() {
type Person struct {
id, age int
address string
}
John := Person{
189,
24,
"四川成都",
}
var p_person *Person
p_person = &John
fmt.Printf("John: %v\n", John)
fmt.Printf("&John: %p\n", &John)
fmt.Printf("p_person: %p\n", p_person)
fmt.Printf("*p_person: %v\n", *p_person)
}
4.7.3 创建结构体指针
可以使用new关键字创建结构体指针,还可以通过使用new关键字对结构体进行实例化,得到的是结构体的地址。
func new_struct() {
type Person struct {
id int
name, address string
}
var p_Lihua = new(Person)
p_Lihua.id = 45
p_Lihua.name = "Lihua"
fmt.Printf("变量p_Lihua的值: %v\n", *p_Lihua)
fmt.Printf("变量p_Lihua存储的指针地址: %p\n", p_Lihua)
fmt.Printf("变量p_Lihua的类型: %T\n", p_Lihua)
}
// 从运行结果看p_Lihua为指针类型
4.7.4 访问结构体指针
访问结构体指针成员,也使用点运算符(.)。
func main() {
type Person struct {
id int
name string
}
var p_person = new(Person)
fmt.Printf("p_person: %T\n", p_person)
p_person.id = 1
p_person.name = "tom"
fmt.Printf("*p_person: %v\n", *p_person)
}
// 运行结果: p_person: *main.Person *p_person: {1 tom}
4.8 结构体作为函数参数
Go结构体可以像普通变量一样,作为函数的参数,传递给函数,这里分为两种情况:
1.直接传递结构体,这是一个副本(拷贝),在函数内部不会改变外面结构体内容。
2.传递结构体指针,这时在函数内部,能够改变外部结构体内容。
4.8.1 直接传递结构体
type Person struct {
id int
name string
}
// 值传递拷贝了一份副本
func showPerson(per Person) {
per.id = 98
per.name = "pite"
fmt.Printf("per: %v\n", per)
}
func main() {
Tom := Person{
id: 100,
name: "Tom",
}
fmt.Printf("Tom: %v\n", Tom)
fmt.Println("------------------")
showPerson(Tom)
fmt.Printf("Tom: %v\n", Tom)
}
// 运行结果:
Tom: {100 Tom}
------------------
per: {98 pite}
Tom: {100 Tom}
// 从运行结果可以看出,函数内部改变了结构体内容,函数外面并没有被改变。
4.8.2 传递结构体指针
type Person struct {
id int
name string
}
func showPerson2(per *Person) {
per.id = 102
per.name = "kite"
}
func main() {
Tom := Person{
id: 100,
name: "Tom",
}
per := &Tom
fmt.Printf("Tom: %v\n", Tom)
showPerson2(per)
fmt.Println("---------------")
fmt.Printf("per: %v\n", *per)
}
// 运行结果:
Tom: {100 Tom}
---------------
per: {102 kite}
// 从运行结果可以看到,调用函数后,参数被改变了。
4.9 嵌套结构体
Go语言没有面向对象编程思想,也没有继承关系,但是可以通过结构体嵌套来实现这种效果。
通过实例演示如何实现结构体嵌套,假如有一个人Person结构体,这个人还养了一个宠物Dog结构体。
// Dog结构体
type Dog struct {
name, color string
age int
}
// Person结构体
type Person struct {
MyDog Dog // Dog结构体类型的变量
name string
age int
}
func main() {
dog := Dog{
name: "毛毛",
age: 2,
color: "黄色",
}
per := Person{
MyDog: dog, // 这里的dog就是上面初始化时声明好的dog
name: "Tom",
age: 32,
}
fmt.Printf("per.MyDog.name: %v\n", per.MyDog.name)
fmt.Printf("per: %v\n", per)
}