Go语言学习16-作用域与内存分析、递归函数

0x00 作用域内存分析

在函数中,变量查找顺序:

1、先在自己函数内部查找,找到后最后的结果就是内部的值

2、内部找不到,就在函数外层寻找,即查找全局变量

3、还找不到就报错:未定义

var x int = 100 //定义一个全局变量
func f1() {
	fmt.Println(x)
}

func main() {

	f1()
}

0x01 作用域分类

全局作用域

var x int = 100 //定义一个全局变量
func f1() {
	fmt.Println(x)
}

func main() {

	f1()
}

函数内部作用域

func f1() {
	var x int = 100
	fmt.Println(x)

}

代码块作用域

if语句中,声明的局部变量

func iff() {
	if y := 35; y > 19 {
		fmt.Println("你好")
	}
}

for语句中,声明的局部变量

func fuck() {
	for i := 10; i < 100; i++ {
		fmt.Println(i)
	}
}
fmt.Println(i)	//这个也无法调用,因为i仅声明在了for循环里面	

0x02 内存分析函数内部定义变量无法在函数外部使用

首先程序运行会进入到main函数,因为main()是入口函数,第一个需要进行执行的函数,优先级高。

在main里面定义了两个变量,正常输出num1,num2=10,20没毛病;

到了调用exchangeNum函数的时候,交换两个数的数值,再回到main函数中,按理说应该换了数值呀,为什么没有换??

func exchangeNum(num1 int, num2 int) {
	var t int
	t = num1
	num1 = num2
	num2 = t

}

func main() {
    num1 := 10
    num2 := 20
	fmt.Printf("交换前的两个数:num1=%v,num2=%v\n", num1, num2)
	exchangeNum(num1, num2)
	fmt.Printf("交换后的两个数:num1=%v,num2=%v\n", num1, num2)
}

image-20220214211127500

内存分析:

当我们运行Go语言时,会向内存申请一块空间,供Go语言运行起来的程序来用。

随后进行逻辑划分,就是分成三个部分。栈、堆、代码区。

基本情况下,栈是用来存放基本数据类型的,如int、string、bool等;堆是用来存放引用数据类型、复杂数据类型的;代码区就是用来存放代码。(再次强调一下,这是一般情况,特殊情况可能堆栈存放的数据会变化)

image-20220214204523493

1、运行代码时,首先是入口main函数,一旦运行main函数,就会在栈里面独自创建出一块区域让函数来存放函数自身的变量等,这块区域被称作为栈帧。

func main() {

image-20220214204804092

2、在main函数中执行声明变量语句,即声明num1,num2;随后在终端输出第一句话

var num1 int = 10
var num2 int = 20
fmt.Printf("交换前的两个数:num1=%v,num2=%v\n", num1, num2)

image-20220214204938556

3、随后调用函数exchangeNum,内存中就会创建exchangeNum栈帧。

exchangeNum(num1, num2)

image-20220214205910795

4、随后进行exchangeNum函数中的第一行语句,开始声明变量num1,num2,并从main函数中继承数值。

func exchangeNum(num1 int, num2 int) {

image-20220214205844178

5、随后进入函数体,声明了t函数,开辟相应的内存空间

var t int

image-20220214205700326

6、随后num1的值被传入到t,t此时被赋值

t = num1	//t = 10

image-20220214205638843

7、再之后,num1的值被替换成20

num1 = num2	//num1 = 20

image-20220214205608974

8、再之后,num2的值会变成10,之后结束函数运行。

num2 = t	//num2 = 10
}

image-20220214211551672

9、当函数运行结束后,会消除掉栈帧,即exchangeNum函数会被销毁。所以exchangeNum函数仅仅只是完成了自身形参的转换罢了,对main函数内部的变量没有任何影响。再之后进行打印,还是这两个数值。

image-20220214210715640

0x03 练习

/*
你有50枚金币,需要分配给以下几个人:Matthew,Sarah,Augustus,Heidi,Emilie,Peter,Giana,Adriano,Aaron,Elizabeth。
分配规则如下:
a. 名字中每包含1个'e'或'E'分1枚金币
b. 名字中每包含1个'i'或'I'分2枚金币
c. 名字中每包含1个'o'或'O'分3枚金币
d: 名字中每包含1个'u'或'U'分4枚金币
写一个程序,计算每个用户分到多少金币,以及最后剩余多少金币?
程序结构如下,请实现 ‘dispatchCoin’ 函数
*/
package main

import "fmt"

var (
	coins = 50
	users = []string{
		"Matthew", "Sarah", "Augustus", "Heidi", "Emilie", "Peter", "Giana", "Adriano", "Aaron", "Elizabeth",
	}
	distribution = make(map[string]int, len(users))
)

func main() {
	left := dispatchCoin()
	fmt.Println("剩下:", left)
}

拿到一个题目,乍一看肯定觉着很难,慢慢分析就好了

package main

import "fmt"

var (
	coins = 50
	users = []string{
		"Matthew", "Sarah", "Augustus", "Heidi", "Emilie", "Peter", "Giana", "Adriano", "Aaron", "Elizabeth",
	}
	distribution = make(map[string]int, len(users))
)

func main() {
	left := dispatchCoin()
	fmt.Println("剩下:", left)
}

func dispatchCoin()(left int){
    //1.依次拿到每个人的名字
    //2.拿到一个人名后,根据分金币的规则去分金币
    //2.1 每个人分的金币数应该保存在map中
    //2.2 还要记录剩余金币数
    //3. 整个第2步执行完就能得到最终每个人的金币数和剩余数
}

0x04 递归函数

说到递归,应该不陌生。著名的rm -rf 就是递归删除,也就是见到一个文件夹,就会打开进去,然后遍历文件名,最后删除。

这么一想,everything这个软件,多半也是这么做的。首先是递归查询目录下的文件,随后进行删除

递归,就是在运行的过程中调用自己。

语法格式如下:

func recursion() {
   recursion() */\* 函数调用自身 \*/*
 }

 func main() {
   recursion()
 }

Go 语言支持递归。但我们在使用递归时,开发者需要设置退出条件,否则递归将陷入无限循环中。

递归函数对于解决数学上的问题是非常有用的,就像计算阶乘,生成斐波那契数列等。

阶乘

5!=5*4*3*2*1

package main

import "fmt"

//递归函数
//计算n=5的阶乘
func f1(n uint64) uint64 {
	//return 5*4!
	if n <= 1 {
		return 1
	}
	return n * f1(n-1)
}

func main() {
	ret := f1(5)
	fmt.Println(ret)
}

0x05 递归函数总结

递归函数:函数自己调用自己

递归适合处理那种问题相同\问题的规模越来越小的场景

递归一定要有一个明确的退出条件

image-20220221164540635

image-20220221165132071

posted @ 2022-02-22 21:24  sukusec不觉水流  阅读(12)  评论(0编辑  收藏  举报