包与模块


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}

注意:

  1. 必须初始化结构体的所有字段。
  2. 初始值的填充顺序必须与字段在结构体中的声明顺序一致。
  3. 该方式不能和键值初始化方式混用。

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)

}

文章作者: 罗宇
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 罗宇 !
 上一篇
方法与接口 方法与接口
Go(Golang)是Google开发的一种静态强类型、编译型语言。语法与C相近,但功能上有:内存安全、GC(垃圾回收),结构形态及CSP-style并发计算。
下一篇 
并发编程 并发编程
Go(Golang)是Google开发的一种静态强类型、编译型语言。语法与C相近,但功能上有:内存安全、GC(垃圾回收),结构形态及CSP-style并发计算。
  目录