日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
Go語言之程序符號重命名

Go程序源代碼中,關(guān)鍵字、接口、類型、常量、變量、方法、函數(shù)、字段、標(biāo)簽(label)等等的名稱都可以稱為符號。

在巴彥淖爾等地區(qū),都構(gòu)建了全面的區(qū)域性戰(zhàn)略布局,加強發(fā)展的系統(tǒng)性、市場前瞻性、產(chǎn)品創(chuàng)新能力,以專注、極致的服務(wù)理念,為客戶提供成都網(wǎng)站制作、成都網(wǎng)站建設(shè) 網(wǎng)站設(shè)計制作按需求定制開發(fā),公司網(wǎng)站建設(shè),企業(yè)網(wǎng)站建設(shè),品牌網(wǎng)站設(shè)計,成都全網(wǎng)營銷,成都外貿(mào)網(wǎng)站建設(shè)公司,巴彥淖爾網(wǎng)站建設(shè)費用合理。

Go可執(zhí)行程序中,符號表主要包含兩種類型的符號:

  1. 數(shù)據(jù)對象(Data object)
  2. 函數(shù)(Function)

一般情況下(不是絕對的),在源代碼編譯為可執(zhí)行程序的過程中,

  • 關(guān)鍵字、局部變量、標(biāo)簽會轉(zhuǎn)變?yōu)橹噶?、?shù)據(jù)或者消失,而不再是符號
  • 接口、類型、全局常量會被保存為不可變數(shù)據(jù),而不再是符號
  • 函數(shù)、方法、全局變量、全局常量會被重命名,保存在符號表中

本文主要總結(jié)了函數(shù)、方法、全局變量在編譯過程中通用的重命名規(guī)則,不討論類似內(nèi)聯(lián)優(yōu)化、閉包、非空接口、編譯器生成等復(fù)雜的情況。

規(guī)則

Go 1.18版本之前符號重命名常見規(guī)則列表如下:

  1. 包名.變量名
  2. 包名.函數(shù)名
  3. 包名.函數(shù)名.funcN
  4. 包名.函數(shù)名.funcN.N
  5. 包名.類型.函數(shù)名
  6. 包名.類型.函數(shù)名.funcN
  7. 包名.類型.函數(shù)名.funcN.N
  8. 包名.(*類型).函數(shù)名
  9. 包名.(*類型).函數(shù)名.funcN
  10. 包名.(*類型).函數(shù)名.funcN.N
  11. 模塊名/包名.變量名
  12. 模塊名/包名.函數(shù)名
  13. 模塊名/包名.函數(shù)名.funcN
  14. 模塊名/包名.函數(shù)名.funcN.N
  15. 模塊名/包名.類型.函數(shù)名
  16. 模塊名/包名.類型.函數(shù)名.funcN
  17. 模塊名/包名.類型.函數(shù)名.funcN.N
  18. 模塊名/包名.(*類型).函數(shù)名
  19. 模塊名/包名.(*類型).函數(shù)名.funcN
  20. 模塊名/包名.(*類型).函數(shù)名.funcN.N
  21. 包名.init
  22. 包名.init.N
  23. 模塊名/包名.init
  24. 模塊名/包名.init.N

以上規(guī)則羅列過于詳細(xì),主要是因為包含了過多的匿名函數(shù)命名規(guī)則;本文會縮小分類粒度進(jìn)行歸納:

  1. 普通函數(shù)
  2. 匿名函數(shù)
  3. 方法
  4. 全局常量
  5. 模塊
  6. 初始化函數(shù)

環(huán)境

 
 
 
 
  1. OS : Ubuntu 20.04.2 LTS; x86_64 
  2. Go : go version go1.16.2 linux/amd64 

代碼清單

完整代碼已經(jīng)上傳到 Github 倉庫:https://github.com/fooree/go-names

目錄和文件結(jié)構(gòu)如下:

go.mod

 
 
 
 
  1. module github.com/fooree/go-names 
  2.  
  3. go 1.16 

main.go

 
 
 
 
  1. package main 
  2.  
  3. import ( 
  4.   "debug/elf" 
  5.   "fmt" 
  6.   "github.com/fooree/go-names/internal" 
  7.   "github.com/fooree/go-names/internal/foo" 
  8.   "github.com/fooree/go-names/internal/foo/ree" 
  9.   "os" 
  10.   "path/filepath" 
  11.   "reflect" 
  12.   "sort" 
  13.   "strings" 
  14.   "time" 
  15.  
  16. //go:noinline 
  17. func anonymousType() { 
  18.   t := reflect.TypeOf(struct { 
  19.     Name string 
  20.   }{ 
  21.     Name: "Jack", 
  22.   }) 
  23.   fmt.Printf("name=%s, string=%s, addres=%p\n", t.Name(), t.String(), t) 
  24.  
  25. func main() { 
  26.   anonymousType() 
  27.   ree.Run() 
  28.   foo.Y.Foo() 
  29.   internal.X.Foo() 
  30.    
  31.   name, _ := filepath.Abs(os.Args[0]) 
  32.   file, err := elf.Open(name) 
  33.   if err != nil { 
  34.     panic(err) 
  35.   } 
  36.   defer func() { _ = file.Close() }() 
  37.   symbols, err := file.Symbols() 
  38.   if err != nil { 
  39.     panic(err) 
  40.   } 
  41.  
  42.   slice := make([]string, 0, 100) 
  43.   for _, symbol := range symbols { 
  44.     const module = "github.com/fooree/go-names" 
  45.     const name = "main" 
  46.     if strings.HasPrefix(symbol.Name, module) || strings.HasPrefix(symbol.Name, name) { 
  47.       slice = append(slice, symbol.Name) 
  48.     } 
  49.   } 
  50.  
  51.   go func() { 
  52.     sort.Slice(slice, func(i, j int) bool { 
  53.       return slice[i] < slice[j] 
  54.     }) 
  55.     go func() { 
  56.       fmt.Println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~") 
  57.     }() 
  58.   }() 
  59.  
  60.   time.Sleep(time.Second) 
  61.  
  62.   for _, sym := range slice { 
  63.     fmt.Println(sym) 
  64.   } 

internal/a.go

 
 
 
 
  1. package internal 
  2.  
  3. import ( 
  4.   "fmt" 
  5.   "reflect" 
  6.  
  7. type Foo interface { 
  8.   Foo() 
  9.  
  10. type Int int 
  11.  
  12. var X Int 
  13.  
  14. //go:noinline 
  15. func (i *Int) Foo() { 
  16.   t := reflect.TypeOf(i) 
  17.   go func() { 
  18.     fmt.Printf("i am Int, name=%s, string=%s\n", t.Name(), t.String()) 
  19.   }() 
  20.  
  21. func init() { 
  22.   X = Int(0x123) 
  23.  
  24. func init() { 
  25.   fmt.Println("X =", X) 

internal/foo/b.go

 
 
 
 
  1. package foo 
  2.  
  3. import ( 
  4.   "fmt" 
  5.   "reflect" 
  6.  
  7. type Ree struct { 
  8.   Name string 
  9.  
  10. //go:noinline 
  11. func (r Ree) Foo() { 
  12.   anonymousType() 
  13.   t := reflect.TypeOf(r) 
  14.   fmt.Printf("i am Ree, name=%s, string=%s\n", t.Name(), t.String()) 
  15.  
  16. //go:noinline 
  17. func anonymousType() { 
  18.   t := reflect.TypeOf(struct { 
  19.     Name string 
  20.   }{ 
  21.     Name: "Jack", 
  22.   }) 
  23.   fmt.Printf("name=%s, string=%s, addres=%p\n", t.Name(), t.String(), t) 
  24.  
  25. var Y = Ree{"Rose"} 
  26.  
  27. func init() { 
  28.   fmt.Println("Y =",Y) 

internal/foo/ree/c.go

 
 
 
 
  1. package ree 
  2.  
  3. import "sort" 
  4.  
  5. var arr = []int{1, 5, 6, 2, 7, 3, 7, 2} 
  6.  
  7. func Run() { 
  8.   sort.Slice(arr, func(i, j int) bool { 
  9.     return arr[i]
  10.   }) 

查看符號表

編譯以上代碼并執(zhí)行,在執(zhí)行過程中,對可執(zhí)行程序本身進(jìn)行符號解析,過濾并輸出以上代碼中定義的符號。

普通函數(shù)

在Go語言中,所有的代碼都必須位于某個包(package)中。

在Go語言中,最特殊的一個包名是main,無論它所在的目錄名稱是什么,編譯后該包下的符號都必須以 main.開頭。

在Go語言中,最特殊的一個函數(shù)是main包中的main函數(shù),其編譯之后的符號名稱為main.main,Go運行時將該函數(shù)作為程序的入口。

main包中的其它有名稱的函數(shù),都會被編譯器重命名為“包名.函數(shù)名”的格式,例如main.anonymousType。

匿名函數(shù)

顧名思義,匿名函數(shù)就是沒有名稱的函數(shù)。

函數(shù)中定義的匿名函數(shù),會被重命名為“包名.函數(shù)名.funcN”的格式,其中N是一個可遞增的數(shù)字。

例如,在以上代碼清單中,

main函數(shù)里defer關(guān)鍵字后的func() { _ = file.Close() }()是第一個匿名函數(shù),被重命名為main.main.func1。

main函數(shù)里go關(guān)鍵字后的func是第二個匿名函數(shù),被重命名為main.main.func2。

main.main.func2函數(shù)里又定義了兩個匿名函數(shù),它們不再被重命名為funcN格式,而是被重命名為funcN.N格式,分別為main.main.func2.1和main.main.func2.2。

方法

專屬于某一個數(shù)據(jù)類型的函數(shù),稱為方法。

方法,只不過是語法層面的一個稱謂而已,其本質(zhì)就是函數(shù);方法的接受者就是其第一個參數(shù),所以方法至少有一個參數(shù)。

在 A Tour of Go (https://tour.golang.org/methods/1) 中,對函數(shù)的定義為:

 
 
 
 
  1. A method is a function with a special receiver argument. 

方法的定義格式有兩種:

1.接受者為數(shù)據(jù)類型

例如,reflect/value.go源文件中的Elem方法:

 
 
 
 
  1. func (v Value) Elem() Value { 
  2.     // 此處省略方法代碼 

2.接受者為指針類型

例如,reflect/value.go源文件中的Value方法:

 
 
 
 
  1. func (it *MapIter) Value() Value {  
  2.     // 此處省略方法代碼 

通常情況下,以上兩種格式的方法定義,對應(yīng)的重命名規(guī)則分別如下:

包名.類型.方法名,例如:reflect.Value.Elem

包名.(*類型).方法名,例如:reflect.(*MapIter).Value

實際情況是:編譯過程中的方法重命名規(guī)則要復(fù)雜的多。后續(xù)其他的專題文章會逐漸介紹。

方法中如果包含匿名函數(shù),重命名規(guī)則是在其后追加funcN或funcN.N。

全局變量

全局變量的重命名規(guī)則是“包名.變量名”。

例如os/proc.go源文件中定義的Args變量。

包層級

在Go語言中,一個包可以包含和定義其他的包,這是通過子目錄實現(xiàn)的,從而形成了包的層級結(jié)構(gòu)。

如果包存在層級結(jié)構(gòu),則使用“/”進(jìn)行包名之間的連接,從而實現(xiàn)包的編譯重命名。

例如,io/fs/源碼目錄中定義包名是fs,該包中的變量和函數(shù),在編譯后它們的包名都是“io/fs”。

模塊

模塊(module)是Go語言的依賴管理工具。

一個模塊一般會包含一個或多個包(package)。

模塊中的包、函數(shù)、方法、全局變量、匿名函數(shù)的重命名規(guī)則與以上總結(jié)的規(guī)則一致,只是需要增加前綴“模塊名/”。

例如,文本代碼清單中定義的模塊名稱是github.com/fooree/go-names,模塊中定義的符號重命名如下:

 
 
 
 
  1. github.com/fooree/go-names/internal.(*Int).Foo        // 方法名 
  2. github.com/fooree/go-names/internal.(*Int).Foo.func1  // 匿名函數(shù) 
  3. github.com/fooree/go-names/internal.X                 // 全局變量 
  4. github.com/fooree/go-names/internal/foo.Ree.Foo       // 方法名 
  5. github.com/fooree/go-names/internal/foo.Y             // 全局變量 
  6. github.com/fooree/go-names/internal/foo.anonymousType // 函數(shù)名 
  7. github.com/fooree/go-names/internal/foo/ree.Run       // 函數(shù)名 
  8. github.com/fooree/go-names/internal/foo/ree.Run.func1 // 匿名函數(shù) 
  9. github.com/fooree/go-names/internal/foo/ree.arr       

初始化函數(shù)

關(guān)于初始化函數(shù)的重命名規(guī)則,請閱讀 【Go】初始化函數(shù)。

結(jié)語

本文總結(jié)了一些基本的符號重命名規(guī)則。

本文轉(zhuǎn)載自微信公眾號「Golang In Memory」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系Golang In Memory公眾號。


本文名稱:Go語言之程序符號重命名
網(wǎng)頁鏈接:http://www.5511xx.com/article/ccodjgp.html