跟着书做项目碰到了不认识的函数
在边看书边敲代码的时候,碰到了一个不认识的函数,在学习 Golang 的基础的时候也没碰到过
但是网上一搜,感觉又特别重要,所以琢磨了一个多小时,总算是明白是拿来做什么的了
这个函数就是 runtime.Caller 及其他一起使用的函数
获取函数信息
在很多文章里, Caller 出现最多的就是在日志部分
因为它可以拿来获取调用者的各种信息,比如名称,行号等
比如如下函数
1 2 3 4 5 6 7 8 9 10 11
| func printName() string { pc, _, _, _ := runtime.Caller(1) return runtime.FuncForPC(pc).Name() }
func printCallerName() string { { pc, _, _, _ := runtime.Caller(2) return runtime.FuncForPC(pc).Name() } }
|
printName()
获取本函数名称
printCallerName()
获取调用函数名称
runtime.Caller 需要传入一个 skip 参数,skip 是堆栈向前推几个的数量
因为函数的调用是压栈操作,0就表示本函数,1就表示向前推一个函数,printName()本身就是一个函数,所以往前推一个就是调用者了
printCallerName()是用来获取调用者的函数,0表示其本身,1表示调用者,2就表示调用者的调用者,也就是我们想获取的调用者
1 2 3 4 5 6 7 8 9 10 11 12
| func main() { hello() }
func hello() { fmt.Printf("我是 %s, %s 在调用我\n", printName(), printCallerName()) world() }
func world() { fmt.Printf("我是 %s, %s 在调用我\n", printName(), printCallerName()) }
|
输出如下
1 2
| 我是 main.hello, main.main 在调用我 我是 main.world, main.hello 在调用我
|
目前碰到的几个函数的使用
func Caller(skip int) (pc uintptr, file string, line int, ok bool)
就是前面使用的函数,需要传入一个 skip 参数表示以本函数为0向前推几个函数
返回4个值,分别为
- pc 指向程序计数器的 uintptr 指针, uintptr 指针不是真正的指针,它不能指向对象,但它可以进行指针的加减,就跟 C 里面的指针一样,使用时需要把它转换为 Pointer,通过*操作来取值,赋值
- file 字符串型,名称
- line int型,行号
- ok 布尔值,用来表示是否成功获取
func Callers(skip int, pc []uintptr) int
将连续的程序计数器放到 pc 指针中,直到指针的空间放不下了为止,skip表示从谁开始,不过0表示 Callers() 函数本身,1才表示本函数,这里和Caller() 是有一点差别的
1 2 3 4 5 6 7 8 9 10 11 12 13
| func main(){ trace() }
func trace() { pc := make([]uintptr, 10) n := runtime.Callers(0, pc) for i := 0; i < n; i++ { f := runtime.FuncForPC(pc[i]) file, line := f.FileLine(pc[i]) fmt.Printf("%s:%d %s\n", file, line, f.Name()) } }
|
这样就可以看到一连串的程序调用列表了
1 2 3 4 5
| /usr/lib/go/src/runtime/extern.go:247 runtime.Callers /usr/lib/go/src/runtime/extern.go:247 runtime.Callers /home/nero/GoProgram/Caller-Learn/main.go:10 main.main /usr/lib/go/src/runtime/proc.go:259 runtime.main /usr/lib/go/src/runtime/asm_amd64.s:1595 runtime.goexit
|
func CallersFrames(callers []uintptr) *Frames
Callers() 只是获取了程序计数器,使用 CallersFrames() 可以获取到整个栈的信息
意思大概就是 Callers 只是获取一个指针,需要的通过指针去寻找并获取,而 CallersFrames() 是把所有信息一次性全部拿过来了
CallersFrames() 将获取到的信息放到 callers 指针中,返回一个 pc 的切片指针
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| func main(){ trace() }
func trace() { pc := make([]uintptr, 10) n := runtime.Callers(0, pc) frames := runtime.CallersFrames(pc[:n]) for { frame, more := frames.Next() fmt.Printf("%s:%d %s\n", frame.File, frame.Line, frame.Function) if !more { break } } }
|
效果一样
1 2 3 4 5
| /usr/lib/go/src/runtime/extern.go:247 runtime.Callers /home/nero/GoProgram/Caller-Learn/main.go:46 main.trace2 /home/nero/GoProgram/Caller-Learn/main.go:9 main.main /usr/lib/go/src/runtime/proc.go:250 runtime.main /usr/lib/go/src/runtime/asm_amd64.s:1594 runtime.goexit
|
敲代码敲着敲着突然卡壳了,不懂这里用的是什么函数,不知道是干什么的 (°ཀ°)
然后就去找这个函数是干什么的,于是一个多小时过去了,总算是大概懂了,也没算浪费时间吧,虽然学习项目的进度又慢了一点 (´A`。)
但学到了新东西就是好,一天学一点嘛 (゚∀゚)