学习自曹大
之前曹大和雨痕大神的书都涉及过一些debugger的高级用法,但是自己还是没用起来,这次借助整理再熟悉下,以后就用起来。
var a = new(T) 和 var a = &T{} 这两种语法有区别么?
type T struct { } func main() { var a = new(T) var b = &T{} println(a, b) }
推荐是使用第二种,但这不是本随笔的重点。我们可以在gdb里看一下,其实不如go tool objdump/compile 清晰,这里只是展示有这个功能,至于分析,明显这两个就不是一个方式实现的,但是开启优化后就会成一样的了。
,(gdb) disassemble main.main Dump of assembler code for function main.main: => 0x00000000004581d0 <+0>: mov %fs:0xfffffffffffffff8,%rcx 0x00000000004581d9 <+9>: cmp 0x10(%rcx),%rsp 0x00000000004581dd <+13>: jbe 0x458240 <main.main+112> 0x00000000004581df <+15>: sub $0x30,%rsp 0x00000000004581e3 <+19>: mov %rbp,0x28(%rsp) 0x00000000004581e8 <+24>: lea 0x28(%rsp),%rbp 0x00000000004581ed <+29>: lea 0xf(%rsp),%rax
// new(T) 0x00000000004581f2 <+34>: mov %rax,0x18(%rsp) 0x00000000004581f7 <+39>: lea 0xf(%rsp),%rax
// &T{} 0x00000000004581fc <+44>: mov %rax,0x20(%rsp) 0x0000000000458201 <+49>: mov %rax,0x10(%rsp)
0x0000000000458206 <+54>: callq 0x42b010 <runtime.printlock> 0x000000000045820b <+59>: mov 0x18(%rsp),%rax 0x0000000000458210 <+64>: mov %rax,(%rsp) 0x0000000000458214 <+68>: callq 0x42b910 <runtime.printpointer> 0x0000000000458219 <+73>: callq 0x42b250 <runtime.printsp> 0x000000000045821e <+78>: mov 0x10(%rsp),%rax 0x0000000000458223 <+83>: mov %rax,(%rsp) 0x0000000000458227 <+87>: callq 0x42b910 <runtime.printpointer> 0x000000000045822c <+92>: callq 0x42b2a0 <runtime.printnl> 0x0000000000458231 <+97>: callq 0x42b090 <runtime.printunlock> 0x0000000000458236 <+102>: mov 0x28(%rsp),%rbp 0x000000000045823b <+107>: add $0x30,%rsp 0x000000000045823f <+111>: retq 0x0000000000458240 <+112>: callq 0x4519d0 <runtime.morestack_noctxt> 0x0000000000458245 <+117>: jmp 0x4581d0 <main.main> End of assembler dump.
查看 go 的 interface 的数据结构
package main import ( "fmt" "io" "os" ) var ( v interface{} r io.Reader f *os.File fn os.File ) func main() { fmt.Println(v == nil) fmt.Println(r == nil) fmt.Println(f == nil) v = r fmt.Println(v == nil) v = fn fmt.Println(v == nil) v = f fmt.Println(v == nil) r = f fmt.Println(r == nil) }
true true true true false false false
是不是很奇怪,为什么都是”nil“接口却最后不等于nil了,解释可以解释接口有两个指针不一样,但是这好像不足以服众,还是用debugger看一下吧。
(gdb) b main.main Breakpoint 1 at 0x491c70: file /root/main.go, line 16. (gdb) r Starting program: /root/main Breakpoint 1, main.main () at /root/main.go:16 16 func main() { (gdb) n 17 fmt.Println(v == nil) (gdb) n true 18 fmt.Println(r == nil) (gdb) n true 19 fmt.Println(f == nil) (gdb) n true 20 v = r (gdb) p r $1 = {tab = 0x0, data = 0x0} (gdb) p v $2 = {_type = 0x0, data = 0x0}
(gdb) p f
$3 = (os.File *) 0x0
在 golang 中空 interface 和非空 interface 在数据结构上是不同的。空 interface 就只有 runtime._type 和 void* 指针组成。而非空 interface 则是 runtime.itab 和 void* 指针组成。
26 r = f (gdb) n 27 fmt.Println(r == nil) (gdb) n false 28 }(gdb) p r $14 = {tab = 0x4dc480 <File,io.Reader>, data = 0x0} (gdb) p v $15 = {_type = 0x4baf00, data = 0x0} (gdb) p *v._type $16 = {size = 8, ptrdata = 8, hash = 871609668, tflag = 9 ' ', align = 8 '', fieldAlign = 8 '', kind = 54 '6', equal = {void (void *, void *, bool *)} 0x4baf00, gcdata = 0x4d8b08 "