Go语言基础Part II | 青训营笔记

118 阅读5分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第2篇笔记.

第二部分 函数,方法和结构体

2.1 函数func

go中的函数形如

func function_name( [parameter list] ) [return_types] {
   函数体
};

函数定义解析

  • func:函数由 func 开始声明
  • function_name:函数名称,参数列表和返回值类型构成了函数签名。
  • parameter list:参数列表,参数就像一个占位符,当函数被调用时,你可以将值传递给参数,这个值被称为实际参数。参数列表指定的是参数类型、顺序、及参数个数。参数是可选的,也就是说函数也可以不包含参数。
  • return_types:返回类型,函数返回一列值。return_types 是该列值的数据类型。有些功能不需要返回值,这种情况下 return_types 不是必须的。
  • 函数体:函数定义的代码集合。

函数调用

当创建函数时,你定义了函数需要做什么,通过调用该函数来执行指定任务。 调用函数,向函数传递参数,并返回值,例如:

func add(a int, b int) int {
   return a + b
}

func add2(a, b int) int {
   return a + b
}

func exists(m map[string]string, k string) (v string, ok bool) {
   v, ok = m[k]
   return v, ok
}

func main() {
   res := add(1, 2)
   fmt.Println(res) // 3

   v, ok := exists(map[string]string{"a": "A"}, "a")
   fmt.Println(v, ok) // A True
}

Go 函数可以返回多个值,例如上文中的exists函数返回了v和ok两个返回值

函数参数

函数如果使用参数,该变量可称为函数的形参, 形参就像定义在函数体内的局部变量; 调用函数可以通过值传递和引用传递来传递参数; 默认情况下,Go 语言使用的是值传递,即在调用过程中不会影响到实际参数。

函数闭包

Go 语言支持匿名函数,可作为闭包。匿名函数是一个"内联"语句或表达式。匿名函数的优越性在于可以直接使用函数内的变量,不必申明。

以下实例中,我们创建了函数 getSequence() ,返回另外一个函数。该函数的目的是在闭包中递增 i 变量,代码如下:

package main

import "fmt"

func getSequence() func() int {
   i:=0
   return func() int {
      i+=1
      return i
   }
}

func main(){
   /* nextNumber 为一个函数,函数 i 为 0 */
   nextNumber := getSequence()

   /* 调用 nextNumber 函数,i 变量自增 1 并返回 */
   fmt.Println(nextNumber())  // 1
   fmt.Println(nextNumber())  // 2
   fmt.Println(nextNumber())  // 3

   /* 创建新的函数 nextNumber1,并查看结果 */
   nextNumber1 := getSequence() 
   fmt.Println(nextNumber1()) // 1
   fmt.Println(nextNumber1()) // 2
}

2.2 指针pointer

package main

import "fmt"

func add2(n int) {
   n += 2
}

func add2ptr(n *int) {
   *n += 2
}

func main() {
   n := 5
   add2(n)
   add2(n)
   fmt.Println(n) // 5
   add2ptr(&n)
   fmt.Println(n) // 7
}

一个指针变量指向了一个值的内存地址。

类似于变量和常量,在使用指针前你需要声明指针。指针声明格式如下:

var var_name *var-type

var-type 为指针类型,var_name 为指针变量名,* 号用于指定变量是作为一个指针。

如何使用指针

指针使用流程:

  • 定义指针变量。
  • 为指针变量赋值。
  • 访问指针变量中指向地址的值。 在指针类型前面加上 * 号(前缀)来获取指针所指向的内容。
package main

import "fmt"

func main() {
   var a int= 20   /* 声明实际变量 */
   var ip *int        /* 声明指针变量 */

   ip = &a  /* 指针变量的存储地址 */
   // 不能用 *ip = a,会报空指针

   fmt.Printf("a 变量的地址是: %x\n", &a  )

   /* 指针变量的存储地址 */
   fmt.Printf("ip 变量储存的指针地址: %x\n", ip )

   /* 使用指针访问值 */
   fmt.Printf("*ip 变量的值: %d\n", *ip )  // 20
}

Go 空指针

当一个指针被定义后没有分配到任何变量时,它的值为 nil。

nil 指针也称为空指针。

nil在概念上和其它语言的null、None、nil、NULL一样,都指代零值或空值。

声明一个指针变量之后,一定要为其赋值,不然会报空指针错误

2.3 结构体struct

Go 语言中数组可以存储同一类型的数据,但在结构体中我们可以为不同项定义不同的数据类型。

结构体是由一系列具有相同类型或不同类型的数据构成的数据集合。

定义结构体

结构体定义需要使用 type 和 struct 语句。struct 语句定义一个新的数据类型,结构体中有一个或多个成员。type 语句设定了结构体的名称。结构体的格式如下:

type struct_variable_type struct {
   member definition
   member definition
   ...
   member definition
}

定义过后的结构体需要实例化才可以使用,实例化时可以指定字段名

访问结构体字段,可以用结构体名.字段名的格式来访问

结构体可以作为函数的参数来传递,且类型为值传递; 如果要在函数体内改变字段的值,则需要传结构体的指针

package main

import "fmt"

type user struct {
   name     string
   password string
}

func main() {
   a := user{name: "wang", password: "1024"}
   b := user{"wang", "1024"}
   c := user{name: "wang"}
   c.password = "1024"
   var d user
   d.name = "wang"
   d.password = "1024"

   fmt.Println(a, b, c, d)                 // {wang 1024} {wang 1024} {wang 1024} {wang 1024}
   fmt.Println(checkPassword(a, "haha"))   // false
   fmt.Println(checkPassword2(&a, "haha")) // false

}

func checkPassword(u user, password string) bool {
   return u.password == password
}

2.4 结构体方法

Go 语言中同时有函数和方法。一个方法就是一个包含了接受者(receiver)的函数,接受者可以是命名类型或者结构体类型的一个值或者是一个指针。所有给定类型的方法属于该类型的方法集。

方法可以在结构体上,也可以在结构体指针上;在指针上的方法可以修改结构体内部字段的值

package main

import "fmt"

type user struct {
   name     string
   password string
}

func (u user) checkPassword(password string) bool {
   return u.password == password
}

func (u *user) resetPassword(password string) {
   u.password = password
}

func main() {
   a := user{name: "wang", password: "1024"}
   a.resetPassword("2048")
   fmt.Println(a.checkPassword("2048")) // true
}