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)
}
内存分析:
当我们运行Go语言时,会向内存申请一块空间,供Go语言运行起来的程序来用。
随后进行逻辑划分,就是分成三个部分。栈、堆、代码区。
基本情况下,栈是用来存放基本数据类型的,如int、string、bool等;堆是用来存放引用数据类型、复杂数据类型的;代码区就是用来存放代码。(再次强调一下,这是一般情况,特殊情况可能堆栈存放的数据会变化)
1、运行代码时,首先是入口main函数,一旦运行main函数,就会在栈里面独自创建出一块区域让函数来存放函数自身的变量等,这块区域被称作为栈帧。
func main() {
2、在main函数中执行声明变量语句,即声明num1,num2;随后在终端输出第一句话
var num1 int = 10
var num2 int = 20
fmt.Printf("交换前的两个数:num1=%v,num2=%v\n", num1, num2)
3、随后调用函数exchangeNum,内存中就会创建exchangeNum栈帧。
exchangeNum(num1, num2)
4、随后进行exchangeNum函数中的第一行语句,开始声明变量num1,num2,并从main函数中继承数值。
func exchangeNum(num1 int, num2 int) {
5、随后进入函数体,声明了t函数,开辟相应的内存空间
var t int
6、随后num1的值被传入到t,t此时被赋值
t = num1 //t = 10
7、再之后,num1的值被替换成20
num1 = num2 //num1 = 20
8、再之后,num2的值会变成10,之后结束函数运行。
num2 = t //num2 = 10
}
9、当函数运行结束后,会消除掉栈帧,即exchangeNum
函数会被销毁。所以exchangeNum
函数仅仅只是完成了自身形参的转换罢了,对main函数内部的变量没有任何影响。再之后进行打印,还是这两个数值。
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 递归函数总结
递归函数:函数自己调用自己
递归适合处理那种问题相同\问题的规模越来越小的场景
递归一定要有一个明确的退出条件