欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Go語言中字符串賦值中的問題與解決方法

 更新時間:2024年12月15日 10:13:06   作者:uccs  
這篇文章主要為大家詳細介紹了Go語言中字符串賦值會出現(xiàn)的一些問題以及解決方法,文中的示例代碼講解詳細,感興趣的小伙伴可以參考一下

字符串的拼接方式

使用 +

使用 + 號拼接字符串的方式,每次拼接都會創(chuàng)建一個新的字符串,然后將原來的字符串復制到新的字符串中,這樣會導致大量的內存分配和復制操作,性能較差。

字符串格式化函數(shù) fmt.Sprintf 函數(shù)

預分配 bytes.Buffer 緩沖區(qū)

func BufferCapResize() {
  var str = "abcd"
  var buf bytes.Buffer
  // 預分配內存
  buf.Grow(4 * 10000) // 如果沒有這一行,當長度不夠了就會擴容
  cap := 0
  for i := 0; i < 10000; i++ {
    if buf.Cap() != cap {
      println("cap:", buf.Cap())
      cap = buf.Cap()
    }
    buf.WriteString(str)
  }
}
  • 預分配 strings.Builder 構建器
  • 預分配 []byte
  • 使用 strings.Join 函數(shù)

strings.Builderbytes.Buffer 底層都是一個字節(jié)數(shù)組,但是 bytes.Buffer 在轉換字符串的時候,需要重新申請內存空間,而strings.Builder 是直接將底層的 bytes 轉換成字符串進行返回

  • string(b.buf[b.off:]) 直接強轉
  • unsafe.String(unsafe.Slice(b.buf), len(b.buf)) 零拷貝轉換

字符串內存泄露

對字符進行截取時指向同一塊內存空間

如何避免:

  • 將子字符串轉換成字節(jié)切片,在轉成 string
  • 截取后再前面拼接一個新字符串
  • 使用 strings.Builder 對新字符串進行重新構造

定義一個很長的字符串 s := strings.Repeat("a", 1<<20),

賦值

打印的是同一個地址 打印的是一個 nil

字符串在賦值的時候不會發(fā)生拷貝,只是改變底層的指針指向

原始的字符串 s 即使被重新賦值為空字符串,但是 s2 依然指向原來的字符串,所以原始的地址不會被釋放

func main() {
  ptr := (*reflect.StringHeader)(unsafe.Pointer(&s))
  fmt.Println("s pointer:", unsafe.Pointer(ptr.Data)) // ① 0xc000180000
  Assign()
}

func Assign() {
  s2 := s
  ptr := (*reflect.StringHeader)(unsafe.Pointer(&s2))
  fmt.Println("Assign:", unsafe.Pointer(ptr.Data))  // ② 0xc000180000

  s := ""
	ptr = (*reflect.StringHeader)(unsafe.Pointer(&s))
	fmt.Println("s pointer", unsafe.Pointer(ptr.Data)) // ③ nil

	ptr = (*reflect.StringHeader)(unsafe.Pointer(&s2))
	fmt.Println("Assign", unsafe.Pointer(ptr.Data))  // ④ 0xc000180000
  _ = s2
}

通過引用賦值

不管是通過引用賦值,還是值賦值,最終都是指向同一個地址

func main() {
  ptr := (*reflect.StringHeader)(unsafe.Pointer(&s))
  fmt.Println("s pointer:", unsafe.Pointer(ptr.Data))  // ① 0xc000180000
  AssignPointer()
}

func AssignPointer() {
  s2 := &s  // 通過引用賦值
  ptr := (*reflect.StringHeader)(unsafe.Pointer(s2))
  fmt.Println("AssignPointer:", unsafe.Pointer(ptr.Data))  // ② 0xc000180000
  _ = s2
}

字符串截取

s2 是截取 s 字符串的前 20 位,這樣 s2s 的起始地址是一樣的

這種解決很容易導致內存泄露,因為字符串 s 申請的空間是非常大的,s 在不使用的情況下,也是不會被回收的,因為 s2 指向了 s 的地址

func main() {
  ptr := (*reflect.StringHeader)(unsafe.Pointer(&s))
  fmt.Println("s pointer:", unsafe.Pointer(ptr.Data))  // ① 0xc000100000
  StringSlice()
}

func StringSlice() {
  s2 := s[:20]
  ptr := (*reflect.StringHeader)(unsafe.Pointer(&s2))
  fmt.Println("StringSlice:", unsafe.Pointer(ptr.Data)) // ② 0xc000100000
  _ = s2
}

字符串傳遞

字符串傳遞到函數(shù)內部,不管是指針傳遞還是值傳遞,字符串實際內容在內存中的地址是相同的

func main() {
  ptr := (*reflect.StringHeader)(unsafe.Pointer(&s))
  fmt.Println("s pointer:", unsafe.Pointer(ptr.Data)) // ① 0x49b7ba
  f1(s)
  f2(&s)
}
func f1(s string) string {
  ptr := unsafe.StringData(s)
  fmt.Println("f1:", ptr) // ② 0x49b7ba
  return s
}
func f2(s *string) *string {
  ptr := unsafe.StringData(*s)
  fmt.Println("f2:", ptr) // ③ 0x49b7ba
  return s
}

你可能會發(fā)現(xiàn),網(wǎng)上說傳遞指針才不會發(fā)生拷貝,傳遞值是會發(fā)生拷貝,但為什么現(xiàn)在無論是傳遞指針還是傳遞值,字符串都沒有發(fā)生拷貝

這是因為 &s 打印的是函數(shù)參數(shù) s 在棧上的地址,每次函數(shù)調用都會不同

func main() {
  ptr := (*reflect.StringHeader)(unsafe.Pointer(&s))
  fmt.Println("s pointer:", unsafe.Pointer(ptr.Data)) // ① 0x49b7ba
  fmt.Println("s 地址:", &s) // 0x528650
  f1(s)
  f2(&s)
}
func f1(s string) string {
	fmt.Println("f1 s:", &s)    // 0xc000014070
  ptr := unsafe.StringData(s)
	fmt.Println("f1:", ptr) // 0x49b7ba
	return s
}
func f2(s *string) *string {
	fmt.Println("f2 s:", s)   // 0x528650
  ptr := unsafe.StringData(*s)
	fmt.Println("f2:", ptr) // 0x49b7ba
	return s
}

改變字符串地址的方式

1.強轉

func StringSlice1(s string) string {
  fmt.Println("string:", unsafe.StringData(s))  // 0xc000100000
  s1 := string([]byte(s[:20]))
  ptr := unsafe.StringData(s1)
  fmt.Println("StringSlice1:", ptr) // 0xc0000bc000
  return s1
}

2.改變首字符,就能改變 s1 的地址

func StringSlice2(s string) string {
  fmt.Println("string:", unsafe.StringData(s))  // 0xc000100000
  s1 := (" " + s[:20])[1:]
  ptr := unsafe.StringData(s1)
  fmt.Println("StringSlice2:", ptr) // 0xc000018199
  return s1
}

3.使用 StringsBuilder 改變 s1 的地址

func StringSliceUseBuilder(s string) string {
  fmt.Println("string:", unsafe.StringData(s)) // 0xc000100000
  var b strings.Builder
  b.Grow(20)
  b.WriteString(s[:20])
  s1 := b.String()
  ptr := unsafe.StringData(s1)
  fmt.Println("StringSliceUseBuilder:", ptr) // 0xc0000b0000
  return s1
}

字符切換零拷貝轉換

雖然同是切片操作,但是 s1 會改變地址,而 s2 不會改變地址

  • s1 強轉為字符串的指針類型
  • s2:先對 s 進行取指,取指之后將它轉成字符串切片指針類型,然后在獲取指針的內容

所以 s2 的方法是零拷貝轉換

func main() {
  ptr := (*reflect.StringHeader)(unsafe.Pointer(&s))
  fmt.Println("s pointer:", unsafe.Pointer(ptr.Data)) // 0xc000180000
}
func stringToBytes() {
  s1 := []byte(s)
  fmt.Println("s1: ", unsafe.SliceData(s1)) // 0xc000280000

  s2 := *(*[]byte)(unsafe.Pointer(&s))
  fmt.Println("s2: ", unsafe.SliceData(s2)) // 0xc000180000
}

到此這篇關于Go語言中字符串賦值中的問題與解決方法的文章就介紹到這了,更多相關Go字符串賦值內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Golang反射修改變量值的操作代碼

    Golang反射修改變量值的操作代碼

    這篇文章主要介紹了Golang反射修改變量值,也就是Golang反射三大定律中的前兩個,即從interface{}到反射對象和從反射對象到interface{},需要的朋友可以參考下
    2022-12-12
  • Go?為什么不支持可重入鎖原理解析

    Go?為什么不支持可重入鎖原理解析

    這篇文章主要為大家介紹了Go?為什么不支持可重入鎖原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-08-08
  • golang?對象深拷貝的常見方式及性能

    golang?對象深拷貝的常見方式及性能

    這篇文章主要介紹了golang?對象深拷貝的常見方式及性能,Go語言中所有賦值操作都是值傳遞,如果結構中不含指針,則直接賦值就是深度拷貝,文章圍繞主題展開更多相關資料,需要的小伙伴可以參考一下
    2022-06-06
  • golang實現(xiàn)一個簡單的websocket聊天室功能

    golang實現(xiàn)一個簡單的websocket聊天室功能

    這篇文章主要介紹了golang實現(xiàn)一個簡單的websocket聊天室功能,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-10-10
  • Golang程序漏洞檢測器govulncheck的安裝和使用

    Golang程序漏洞檢測器govulncheck的安裝和使用

    govulncheck 是一個命令行工具,可以幫助 Golang 開發(fā)者快速找到項目代碼和依賴的模塊中的安全漏洞,該工具可以分析源代碼和二進制文件,識別代碼中對這些漏洞的任何直接或間接調用,本文就給大家介紹一下govulncheck安裝和使用,需要的朋友可以參考下
    2023-09-09
  • Golang壓縮Jpeg圖片和PNG圖片的操作

    Golang壓縮Jpeg圖片和PNG圖片的操作

    這篇文章主要介紹了Golang壓縮Jpeg圖片和PNG圖片的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • 關于go語言載入json可能遇到的一個坑

    關于go語言載入json可能遇到的一個坑

    Go 語言從新手到大神,每個人多少都會踩一些坑,那么下面這篇文章主要給大家介紹了關于go語言載入json可能遇到的一個坑,文中通過示例代碼介紹的非常詳細,對大家具有一定的參考學習價值,需要的朋友們下面來一起看看吧。
    2017-07-07
  • Go 結構體、數(shù)組、字典和 json 字符串的相互轉換方法

    Go 結構體、數(shù)組、字典和 json 字符串的相互轉換方法

    今天小編就為大家分享一篇Go 結構體、數(shù)組、字典和 json 字符串的相互轉換方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-08-08
  • go語言csrf庫使用實現(xiàn)原理示例解析

    go語言csrf庫使用實現(xiàn)原理示例解析

    這篇文章主要為大家介紹了go語言csrf庫使用實現(xiàn)原理示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-10-10
  • Go語言規(guī)范context?類型的key用法示例解析

    Go語言規(guī)范context?類型的key用法示例解析

    這篇文章主要為大家介紹了Go語言規(guī)范context?類型的key用法示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-08-08

最新評論