【golang学习之旅】复杂数据类型——指针 & 函数

系列文章

【golang学习之旅】使用VScode安装配置Go开发环境
【golang学习之旅】报错:a declared but not used
【golang学习之旅】Go 的基本数据类型
【golang学习之旅】深入理解字符串string数据类型
【golang学习之旅】go mod tidy
【golang学习之旅】记录一次 panic case : reflect: reflect.Value.SetInt using unaddressable value
【golang学习之旅】记录一次 error case : full error output: cc1: error: unrecognized command line option
【golang学习之旅】Go程序快速开始 & Go程序开发的基本注意事项
【golang学习之旅】Go语言常用转义字符
【golang学习之旅】Go中的变量(1)
【golang学习之旅】Go中的变量——基本数据类型(1)
【golang学习之旅】Go中的变量——基本数据类型(2)



指针

基本数据类型,变量存的就是值,所以也叫值类型。

指针类型,指针变量存的是一个地址,这个地址所指向的空间存的才是值

package main

import "fmt"

func main() {
	var a int = 10
	fmt.Println("a 的地址:", &a)
	// ptr是一个指针变量,它的类型是*int,它指向int类型的变量a
	var ptr *int = &a
	fmt.Printf("ptr的值: %v, ptr 的地址: %v\n", ptr, &ptr)
	fmt.Println("ptr指向的值:", *ptr)
	// 修改ptr指向的值
	*ptr = 20
	fmt.Println("修改后a的值:", a)
}

输出:
a 的地址: 0xc00000a0b8
ptr的值: 0xc00000a0b8, ptr 的地址: 0xc00005a028
ptr指向的值: 10
修改后a的值: 20
  • 获取变量的地址使用&符号

  • 获取指针类型所指向的值,使用*

  • 可以通过指针修改所指向变量的值

练习:

在这里插入图片描述

值类型有:基本数据类型(int系列,float系列,bool,string)、复杂数据类型中的复合数据类型(数组和结构体)

引用类型有:非值类型就是引用类型


函数

基本语法

func 函数名(形参列表) (返回值列表){
    执行语句...
}
  • 函数声明包含一个函数名参数列表返回值列表函数体。如果函数没有返回值,则返回列表可以省略
  • 函数可以没有参数或接受多个参数
  • 函数可以返回任意数量的返回值。有返回值的函数,必须有明确的终止语句,否则会引发编译错误

在这里插入图片描述

Go中的每一个文件都属于一个包,也就是说Go是以包的形式来管理文件和项目目录结构的。

包的作用

  • 区分相同名字的函数、变量等标识符。不同包的文件中允许重名的标识符
  • 当程序文件很多时,可以很好的管理项目。比如module包、dao包、service包等等
  • 控制函数、变量等访问范围

包的注意事项

  1. 文件的包名通常和文件所在的文件夹名一致,一般为小写字母。

    在这里插入图片描述

  2. 当一个文件要使用其他包的函数或变量时,需先引入对应的包

    • 引入方式1:import “包名/包路径”

    • 引入方式2:

      import (
          "包名"
          "包名"
      )
      

      在这里插入图片描述

  3. package指令在文件第一行,然后是import指令。接下来才是变量、函数声明等等

  4. 在访问其他包函数、变量时,其语法是包名.函数名

    在这里插入图片描述

  5. 如果包名较长,Go支持给包取别名。取别名后,原来的包名就不能使用了

    在这里插入图片描述

  6. 在同一个包下,不能有相同的函数名,否则报重复定义

函数注意事项和细节讨论

  1. 函数的形参列表可以是多个,返回值也可以是多个

  2. 函数的命名遵循标识符命名规范,首字母不能是数字。首字母是大写则该函数可以被其他包访问到,如果是小写,只能被本包文件使用

  3. 基本数据类型和数组默认都是值传递的,即进行值拷贝。在函数内修改值不会影响原值

package main

import (
	"fmt"
)

func test(name string) {
	name = "test"
	fmt.Println("test() ", name)
}
func main() {
	name := "main"
	test(name)
	fmt.Println("main() ", name)
}

输出:
test()  test
main()  main
  1. 如果希望函数内的变量能够修改函数外的变量(指的是以值传递的方式的基本数据类型),可以传入变量的地址,函数内以指针的方式操作变量(从效果上类似引用数据类型的使用)

    package main
    
    import (
    	"fmt"
    )
    
    func test(name *string) {
    	*name = "test"
    	fmt.Println("test() ", *name)
    }
    func main() {
    	name := "main"
    	test(&name)
    	fmt.Println("main() ", name)
    }
    
    输出:
    test()  test
    main()  test
    
  2. 在Go中,函数本身也是一种数据类型,可以赋值给一个变量,通过该变量可以实现对函数的调用

    package main
    
    import (
    	"fmt"
    )
    
    func getSum(n1, n2 int) int {
    	return n1 + n2
    }
    func main() {
    	a := getSum
    	fmt.Printf("a 的类型 %T\n", a)
    
    	res := a(1, 2) // 等价于 res := getSum(1, 2)
    	fmt.Printf("res 的值 %d\n", res)
    }
    
    输出:
    a 的类型 func(int, int) int
    res 的值 3
    
  3. 为了简化数据定义,Go支持自定义数据类型。相当于一个别名

    比如:type myInt int //这时myInt就等价于int来使用了

  4. Go函数支持对返回值命名

    在这里插入图片描述

  5. 使用 _ 标识符时表示忽略该值,可以用作占位符

  6. Go支持可变参数。这里的可变参数实际上是slice切片,通过 args[index]的方式来访问各个值

    package main
    
    import (
    	"fmt"
    )
    
    func getSum(num ...int) int {
    	sum := 0
    	for i := 0; i < len(num); i++ {
    		sum += num[i]
    	}
    	return sum
    }
    func main() {
    	sum := getSum(1, 2, 3, 4)
    	fmt.Println(sum)
    }
    
    输出:
    10
    

init()函数

每一个源文件都可以包含一个init()函数,该函数会在main函数执行前,被Go运行框架调用。

package main

import (
	"fmt"
)

func init() {
	fmt.Println("init()...")
}

func main() {
	fmt.Println("main()...")
}

输出:
init()...
main()...
  1. 如果一个文件同时包含全局变量定义、init函数和main函数,那么执行的顺序是 : 全局变量定义->init函数->main函数
  2. init()函数最主要的作用是完成一些初始化的工作

匿名函数

使用方式:

  1. 在定义匿名函数时就直接调用
  2. 将匿名函数赋给一个变量(函数变量),再通过该变量来调用匿名函数
package main

import (
	"fmt"
)

func main() {
	// 求两数之和
	// 匿名函数方式1
	res1 := func(n1, n2 int) int {
		return n1 + n2
	}(10, 20)
	fmt.Println("匿名函数方式1...res1 = ", res1)
	// 匿名函数方式2
	func1 := func(n1, n2 int) int {
		return n1 + n2
	}
	res2 := func1(10, 20)
	fmt.Println("匿名函数方式2...res2 = ", res2)
}

输出:
匿名函数方式1...res1 =  30
匿名函数方式2...res2 =  30

闭包

闭包就是第一个函数和与其相关的引用环境组合的一个整体(实体)。

现在开始通过例子来说明闭包:

func incr() func() int {
	var x int
	return func() int {
		x++
		return x
	}
}

调用这个函数会返回一个函数变量。

i := incr():通过把这个函数变量赋值给 ii 就成为了一个闭包

所以 i 保存着对 x 的引用,可以想象 i 中有着一个指针指向 xi 中有 x 的地址

由于 i 有着指向 x 的指针,所以可以修改 x,且保持着状态:

println(i()) // 1
println(i()) // 2
println(i()) // 3

也就是说,x 逃逸了,它的生命周期没有随着它的作用域结束而结束

但是这段代码却不会递增:

println(incr()()) // 1
println(incr()()) // 1
println(incr()()) // 1

这是因为这里调用了三次 incr(),返回了三个闭包,这三个闭包引用着三个不同的 x,它们的状态是各自独立的。

  • 22
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不怕娜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值