自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Go語言之程序符號重命名

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

[[427663]]

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

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

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

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

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

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

規(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ī)則羅列過于詳細,主要是因為包含了過多的匿名函數(shù)命名規(guī)則;本文會縮小分類粒度進行歸納:

  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.Namename) { 
  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]<arr[j] 
  10.   }) 

查看符號表

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

普通函數(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關鍵字后的func() { _ = file.Close() }()是第一個匿名函數(shù),被重命名為main.main.func1。

main函數(shù)里go關鍵字后的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.     // 此處省略方法代碼 

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

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

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

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

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

全局變量

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

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

包層級

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

如果包存在層級結(jié)構(gòu),則使用“/”進行包名之間的連接,從而實現(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ù)

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

結(jié)語

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

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

 

責任編輯:武曉燕 來源: Golang In Memory
相關推薦

2020-12-31 09:06:44

Go語言Reflect

2021-10-23 06:42:14

Go語言接口

2021-10-03 22:18:14

Go語言整數(shù)

2021-10-16 17:53:35

Go函數(shù)編程

2022-03-28 13:34:26

Go泛型部署泛型

2010-11-19 13:48:18

2024-01-05 20:46:14

2013-08-20 10:11:20

Go系統(tǒng)管理員

2021-05-12 08:53:54

Go語言調(diào)度

2024-09-04 08:02:12

2021-10-18 10:53:26

Go 代碼技術

2012-06-20 15:01:04

Web

2013-03-22 15:40:32

VS項目整體命名.NET

2011-03-04 09:48:21

PureFTPd

2009-12-22 15:28:51

Linux批量重命名文

2024-01-08 08:23:07

Go語言代碼

2012-08-07 09:29:09

程序員編程

2021-05-27 08:47:16

C語言C語言程序開發(fā)

2018-06-25 13:10:16

Linux復制重命名

2013-07-10 11:11:05

PythonGo語言
點贊
收藏

51CTO技術棧公眾號