成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Go內存中的字符串操作

開發 前端
內存中的字符串類型 詳細描述了字符串在內存中的結構及其類型信息。本文主要研究字符串的各種操作(語法糖),在內存中實際的樣子。

[[423000]]

內存中的字符串類型詳細描述了字符串在內存中的結構及其類型信息。

本文主要研究字符串的各種操作(語法糖),在內存中實際的樣子。

環境

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

聲明

操作系統、處理器架構、Go版本不同,均有可能造成相同的源碼編譯后運行時的寄存器值、內存地址、數據結構不同。

本文僅保證學習過程中的分析數據在當前環境下的準確有效性。

操作類型

比較

  • 相等性比較
  • 不等性比較

連接(相加)

與[]byte的轉換

與[]byte的拷貝

代碼清單

  1. package main 
  2.  
  3. import ( 
  4.   "fmt" 
  5.  
  6. func main() { 
  7.   var array [20]byte 
  8.   var s = "copy hello world" 
  9.   string2slice(s) 
  10.   copyString(array[:], s) 
  11.   slice2string(array[:]) 
  12.   compare() 
  13.   concat() 
  14.  
  15. //go:noinline 
  16. func copyString(slice []byte, s string) { 
  17.   copy(slice, s) 
  18.   PrintSlice(slice) 
  19.  
  20. //go:noinline 
  21. func string2slice(s string) { 
  22.   PrintSlice([]byte(s)) 
  23.  
  24. //go:noinline 
  25. func slice2string(slice []byte) { 
  26.   PrintString(string(slice)) 
  27.  
  28. //go:noinline 
  29. func compare() { 
  30.   var h = "hello" 
  31.   var w = "world!" 
  32.   PrintBool(h > w) 
  33.   PrintBool(h < w) 
  34.   PrintBool(h >= w) 
  35.   PrintBool(h <= w) 
  36.   PrintBool(h != w) // PrintBool(true
  37.   PrintBool(h == w) // PrintBool(false
  38.   PrintBool(testEqual(h, w)) 
  39.   PrintBool(testNotEqual(h, w)) 
  40.  
  41. //go:noinline 
  42. func testEqual(h, w string) bool { 
  43.   return h == w 
  44.  
  45. //go:noinline 
  46. func testNotEqual(h, w string) bool { 
  47.   return h != w 
  48.  
  49. //go:noinline 
  50. func concat() { 
  51.   hello := "hello " 
  52.   world := "world" 
  53.   jack := "Jack" 
  54.   rose := " Rose " 
  55.   lucy := "Lucy" 
  56.   lily := " Lily " 
  57.   ex := "!" 
  58.   PrintString(concat2(hello, world)) 
  59.   PrintString(concat3(hello, jack, ex)) 
  60.   PrintString(concat4(hello, jack, rose, ex)) 
  61.   PrintString(concat5(hello, jack, rose, lucy, lily)) 
  62.   PrintString(concat6(hello, jack, rose, lucy, lily, ex)) 
  63.  
  64. //go:noinline 
  65. func concat2(a, b string) string { 
  66.   return a + b 
  67.  
  68. //go:noinline 
  69. func concat3(a, b, c string) string { 
  70.   return a + b + c 
  71.  
  72. //go:noinline 
  73. func concat4(a, b, c, d string) string { 
  74.   return a + b + c + d 
  75.  
  76. //go:noinline 
  77. func concat5(a, b, c, d, e string) string { 
  78.   return a + b + c + d + e 
  79.  
  80. //go:noinline 
  81. func concat6(a, b, c, d, e, f string) string { 
  82.   return a + b + c + d + e + f 
  83.  
  84. //go:noinline 
  85. func PrintBool(v bool) { 
  86.   fmt.Println("v =", v) 
  87.  
  88. //go:noinline 
  89. func PrintString(v string) { 
  90.   fmt.Println("s =", v) 
  91.  
  92. //go:noinline 
  93. func PrintSlice(s []byte) { 
  94.   fmt.Println("slice =", s) 
  • 添加go:noinline注解避免內聯,方便指令分析
  • 定義PrintBool/PrintSlice/PrintString函數避免編譯器插入runtime.convT*函數調用

深入內存

字符串轉[]byte

代碼清單中的string2slice函數代碼非常簡單,用于觀察[]byte(s)具體實現邏輯,編譯之后指令如下:

圖片

可以清晰地看到,我們在代碼中的[]byte(s),被Go編譯器替換為runtime.stringtoslicebyte函數調用。

runtime.stringtoslicebyte函數定義在runtime/string.go源碼文件中,Go編譯器傳遞給該函數的buf參數值為nil。

  1. func stringtoslicebyte(buf *tmpBuf, s string) []byte { 
  2.   var b []byte 
  3.   if buf != nil && len(s) <= len(buf) { 
  4.     *buf = tmpBuf{} 
  5.     b = buf[:len(s)] 
  6.   } else { 
  7.     b = rawbyteslice(len(s)) 
  8.   } 
  9.   copy(b, s) 
  10.   return b 

rawbyteslice函數的功能是申請一塊內存用于存儲拷貝后的數據。

[]byte轉字符串

代碼清單中的slice2string函數代碼非常簡單,用于觀察string(slice)具體實現邏輯,編譯之后指令如下:

可以清晰地看到,我們在代碼中的string(slice),被Go編譯器替換為runtime.slicebytetostring函數調用。

runtime.slicebytetostring函數定義在runtime/string.go源碼文件中,Go編譯器傳遞給該函數的buf參數值為nil。

拷貝字符串到[]byte

代碼清單中的copyString函數代碼非常簡單,用于觀察copy(slice, s)具體實現邏輯,編譯之后指令如下:

這個邏輯稍微復雜一點點,將以上指令再次翻譯為Go偽代碼如下:

  1. func copyString(slice reflect.SliceHeader, s reflect.StringHeader) { 
  2.     n := slice.Len 
  3.     if slice.Len > s.Len { 
  4.         n = s.Len 
  5.     } 
  6.     if slice.Data != s.Data { 
  7.         runtime.memmove(slice.Data, s.Data, n) 
  8.     } 
  9.     PrintSlice(*(*[]byte)(unsafe.Pointer(&slice))) 

可以看到,Go編譯器在copy(slice, s)這個簡單易用語法糖背后做了很多的工作。

經過比較,以上偽代碼與runtime/slice.go源碼文件中的slicecopy函數非常相似,但又不完全一致。

不等性比較

代碼清單中的compare函數測試了兩個字符串的各種比較操作。

查看該函數的指令,發現Go編譯器將以下四種比較操作全部轉換為runtime.cmpstring函數調用:

  • >
  • <
  • >=
  • <=

runtime.cmpstring函數是一個編譯器函數,不會被直接調用,聲明在cmd/compile/internal/gc/builtin/runtime.go源碼文件中,由匯編語言實現。

GOARCH=amd64的實現位于internal/bytealg/compare_amd64.s源碼文件中。

該函數返回值可能是:

然后使用cmp匯編指令將返回值與0進行比較,再使用以下匯編指令保存最終的比較結果(true / false):

在本例中,有兩個特殊的比較,分別被編譯為單條指令:

  • h != w 被編譯為 movb $0x1,(%rsp)
  • h == w 被編譯為 movb $0x0,(%rsp)

這是因為在本例中編譯器知道"hello"與"world"兩個字符串不相等,所以直接在編譯的時候直接把比較結果編譯到機器指令中。

所以,在代碼定義了testEqual和testNotEqual函數用于比較字符串變量。

相等性比較

關于相等性比較,在 內存中的字符串類型 中已經做了非常詳細的分析和說明。

在本文的代碼清單中,testEqual函數指令如下,與runtime.strequal函數一致,是因為編譯器將runtime.strequal函數內聯(inline)到了testEqual函數中。

出乎意料的是,!=與==編譯后的幾乎一致,只是兩處指令對結果進行了相反的操作:

字符串連接(相加)

在本文的代碼清單中,concat函數用于觀察字符串的連接(+)操作,測試結果表明:

  • 2個字符串相加,實際調用runtime.concatstring2函數
  • 3個字符串相加,實際調用runtime.concatstring3函數
  • 4個字符串相加,實際調用runtime.concatstring4函數
  • 5個字符串相加,實際調用runtime.concatstring5函數
  • 超過5個字符串相加,實際調用runtime.concatstrings函數

以上這些函數調用,都是Go編譯器的代碼生成和插入工作。

在插入runtime.concatstring*函數的過程中,編譯器傳遞給這些函數的buf參數的值為nil。

runtime.concatstring*函數的實現非常簡單,這里不再進一步贅述。

小結

從以上詳細的分析可以看到,我們在開發過程中,所有對字符串進行的簡單操作,都會被Go編譯器編碼為復雜的指令和函數調用。

許多開發者喜歡使用Go進行開發,理由是Go語言非常簡單、簡潔。

是的,我們都喜歡這種甜甜的語法糖。

而且,發掘語法糖背后的秘密,也是很好玩的事。

本文轉載自微信公眾號「Golang In Memory」

責任編輯:姜華 來源: Golang In Memory
相關推薦

2021-09-07 09:23:07

C++字符串算法

2010-09-06 17:30:46

SQL函數

2015-06-09 14:43:36

javascript操作字符串

2024-04-01 08:41:39

字符串.NET

2010-03-16 10:58:35

Python字符串

2009-11-27 10:24:25

PHP字符串操作

2009-07-15 17:20:45

Jython字符串

2021-03-08 08:57:00

Go 字符串測試

2024-10-30 16:49:00

Python字符串

2010-03-11 19:34:57

Python字符串

2010-07-14 12:57:59

Perl字符串

2010-06-28 15:18:51

SQL Server

2022-07-18 08:18:11

字符JavaJDK

2009-08-24 13:04:44

操作步驟C#字符串

2021-10-14 15:34:48

C語言字符串函數

2022-12-08 12:05:03

Bash字符串

2023-12-11 07:33:05

Go語言字符技巧

2023-01-03 08:07:33

Go字符串指針

2021-03-11 18:44:39

字符串SQL表達式

2019-12-25 15:41:50

JavaScript程序員編程語言
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 特黄色一级毛片 | 日本三级播放 | 日韩成人在线观看 | 亚洲欧美在线免费观看 | 久久中文网 | 欧美日韩高清一区二区三区 | 久久久亚洲一区 | 色综合久| 国产精品精品久久久 | 在线国产视频观看 | 97免费视频在线观看 | 成人国产在线视频 | 欧美日韩中文在线 | 免费在线成人 | 一级少妇女片 | 天堂色网 | 99精品视频免费在线观看 | 夜夜精品浪潮av一区二区三区 | 中文字幕精品一区二区三区精品 | 亚洲福利一区 | 色综久久| 亚洲系列第一页 | 亚洲 欧美 另类 综合 偷拍 | 一区二区成人在线 | 久产久精国产品 | 中文字幕视频在线观看 | 国产毛片久久久 | 久久一级 | 欧美一区二区三区高清视频 | 热99视频| 日韩欧美精品一区 | 91大神xh98xh系列全部 | 成人一区二区三区 | 亚洲精品久久久一区二区三区 | 久久国产亚洲 | 国产三级大片 | 精品久久久久久久久久久 | 国产高清在线精品一区二区三区 | 日本成人在线网址 | 久久久久国产精品一区三寸 | 国产精品成人国产乱 |