空のインターフェイスについて少し。 内部のクイックルック

みなさんこんにちは!



警告:この記事はプロにとって新しいものをもたらすものではありませんが、初心者には役立ちます。



あなたがこれを読んだら、 私はすでに死んでいます;少なくともあなたはGo言語に興味があります。 したがって、インターフェイス{}のようなものについて知っています。 そして、インターフェイスが単なる構造であると言ったらどうなりますか? また、独自のインターフェイスを実装するのは非常に簡単ですか? 猫をお願いします。



最も単純なものから始めましょう。 Goで簡単なプログラムを書きましょう。



package main import "fmt" func ifacePrint(a interface{}) { fmt.Println(a) } func main() { ifacePrint("Habrahabr") }
      
      





すべてが単純です-mainは、ifacePrintメソッドを呼び出し、実際にはifacePrintを呼び出します。ifacePrintは、タイプ{}の1つの引数を明示的に受け取ります。



実行すると、文字列「Habrahabr」の出力が取得されます。 しかし、私たちは気にしません

したがって、gdbの監督下でプログラムを起動します。



そして、main.go && gdb ./mainをビルドします。 メソッドにブレークポイントを設定します。



 (gdb) b main.ifacePrint Breakpoint 1 at 0x401000: file /tmp/main.go, line 5.
      
      





そしてプログラムを実行します:



 (gdb) run Starting program: /tmp/main [New LWP 4892] [New LWP 4893] [New LWP 4894] [New LWP 4895] Breakpoint 1, main.ifacePrint (a=...) at /tmp/main.go:5 5 func ifacePrint(a interface{}) {
      
      





gdbはすぐに実行を停止し、ブレークポイントに到達します。 持っている変数のタイプを見てみましょう:



 (gdb) whatis a type = interface {}
      
      





さて、論理的、インターフェイス{}、さて、見て、その内容には何かありますか?



 (gdb) pa $1 = {_type = 0x4b8e00, data = 0xc8200761b0}
      
      





興味深いターン、文字列を送信する必要がありました...しかし、これは文字列、またはむしろそのインターフェイスです。データフィールドには、文字列が含まれるメモリ内の領域へのポインタがあり、_typeは内部(実行時)TypeDescriptor構造体へのポインタです。



将来を見据えて、Go on gOS(gccgo)のインターフェイスをどのように作成したかを示します。



 //   type EmptyInterface struct { __type_descriptor *TypeDescriptor //    -   __object uintptr //    } //  -   type TypeDescriptor struct { kind uint8 //  ,    ID align uint8 //   fieldAlign uint8 //  ( ) size uintptr // hash uint32 hashfn uint32 //TODO equalfn uint32 //TODO gc uintptr //TODO string *string //   ("string", "int") uncommonType *Uncommon //      ptrToThis *TypeDescriptor //      } //     type Uncommon struct { name *string //   pkgPath *string // ,    methods uintptr //TODO    ,        }
      
      





「クラシック」Goでは、すべてがほぼ同じです。



 (gdb) p *a._type $7 = {size = 16, ptrdata = 8, hash = 3774831796, _unused = 0 '\000', align = 8 '\b', fieldalign = 8 '\b', kind = 24 '\030', alg = 0x582860 <runtime.algarray+224>, gcdata = 0x52a3ac "\001\002\003\004\005\006\a\b\t\n\r\016\017\020\022\025\026\031\032\033\037,568<?AUr~\236\237\325\365\370\377", _string = 0x50e5e0, x = 0x4b8e40, ptrto = 0x4b1ba0}
      
      





ここでは、空のインターフェイスの背後にあるデータタイプ「非表示」に関するほぼすべての情報が表示されます。



 (gdb) p a._type._string $11 = (struct string *) 0x50e5e0 (gdb) p *a._type._string $12 = 0x4fdfb0 "string" (gdb) p *a._type.x $13 = {name = 0x50e5e0, pkgpath = 0x0, mhdr = {array = 0x4b8e68, len = 0, cap = 0}} (gdb) p *a._type.x.name $14 = 0x4fdfb0 "string" (gdb) p *a._type.ptrto $15 = {size = 8, ptrdata = 8, hash = 1511480045, _unused = 0 '\000', align = 8 '\b', fieldalign = 8 '\b', kind = 54 '6', alg = 0x5827d0 <runtime.algarray+80>, gcdata = 0x52a3ac "\001\002\003\004\005\006\a\b\t\n\r\016\017\020\022\025\026\031\032\033\037,568<?AUr~\236\237\325\365\370\377", _string = 0x5045d0, x = 0x0, ptrto = 0x0} (gdb) p *a._type.ptrto._string $16 = 0x4fc2b8 "*string" (gdb) p *a._type.alg $17 = {hash = {void (void *, uintptr, uintptr *)} 0x582860 <runtime.algarray+224>, equal = {void (void *, void *, bool *)} 0x582860 <runtime.algarray+224>} (gdb) p *a._type.alg.hash $18 = {void (void *, uintptr, uintptr *)} 0x582860 <runtime.algarray+224> (gdb) p *a._type.alg.equal $19 = {void (void *, void *, bool *)} 0x582868 <runtime.algarray+232>
      
      





このメモでストーリーを完成させることができると思います。 私はあなたを少し啓発し、掘る方向と掘るかどうかを示しました-みんなが自分で決めるようにしてください;)



PSランタイムGoに対処したいすべての人に、gdbでプログラムを選択し、gccgoとGoのソースを読むことをお勧めします。



All Articles