深入理解Go語言切片:共享內存與append()
陷阱
大家好!歡迎回到我的博客。 ?如果您在這裡,您可能剛接觸Golang,或者您是經驗豐富的開發者,想深入了解切片的內部工作原理。那麼,讓我們開始吧!
Go語言因其簡潔性和高效性而備受讚譽——正如人們常說的那樣,“Go語言就是能完成工作”。對於我們這些來自C、C 或Java等語言的開發者來說,Go語言簡潔明了的語法和易用性令人耳目一新。然而,即使在Go語言中,某些特性也可能讓開發者感到困惑,尤其是在處理切片和子切片時。讓我們揭開這些細微之處,更好地理解如何避免append()
和切片共享內存的常見陷阱。
Go語言中的切片是什麼?
通常,當您需要一種數據結構來存儲一系列值時,切片是Go語言中的首選。它們的靈活性來自於這樣一個事實:它們的長度不是其類型的一部分。此特性克服了數組的限制,使我們能夠創建一個可以處理任何大小切片的單個函數,並使切片能夠根據需要增長或擴展。
雖然切片與數組有一些相似之處,例如都是可索引的並且具有長度,但它們在數據管理方式上有所不同。切片充當對底層數組的引用,該數組實際上存儲切片的數據。從本質上講,切片提供對該數組的某些或所有元素的視圖。因此,當您創建一個切片時,Go會自動處理創建保存切片元素/數據的底層數組。
切片的共享內存
數組是連續的內存塊,但使切片有趣的是它們如何引用此內存。讓我們分解切片的結構:
type slice struct { array unsafe.Pointer // 指向底层数组的指针 len int // 切片中的元素数量 cap int // 底层数组的容量 }
當您創建一個切片時,它包含三個組件:
- 指向底層數組的指針
-
len
切片的長度(它包含的元素數量) -
cap
容量(在需要增長之前它可以包含的元素數量)
這就是事情變得有趣的地方。如果您有多個派生自同一數組的切片,則通過一個切片進行的更改將體現在其他切片中,因為它們共享相同的底層數組。
讓我們看下面的例子:
package main import "fmt" func main() { // 创建一个具有初始值的切片 original := []int{1, 2, 3, 4, 5} // 创建一个子切片——两个切片共享相同的底层数组! subslice := original[1:3] fmt.Println("未修改的子切片:", subslice) // 输出 => 未修改的子切片: [2 3] // 修改子切片 subslice[0] = 42 fmt.Println("原始切片:", original) // 输出 => 原始切片: [1 42 3 4 5] fmt.Println("修改后的子切片:", subslice) // 输出 => 修改后的子切片: [42 3] }
理解切片容量
在我們進一步深入之前,讓我們嘗試理解切片容量cap()
。當您從現有的Go語言切片中獲取子切片時,新子切片的容量由原始切片從子切片開始位置的剩餘容量決定。讓我們稍微分解一下:
當您從數組創建切片時,切片的長度是它最初包含的元素數量,而它的容量是它在需要增長之前可以包含的元素總數。
獲取子切片
當您從現有切片中獲取子切片時:
- 子切片的長度是您指定的元素數量。
- 容量計算為原始切片的容量減去子切片的起始索引。
讓我們看一個詳細的例子:
type slice struct { array unsafe.Pointer // 指向底层数组的指针 len int // 切片中的元素数量 cap int // 底层数组的容量 }
- 原始切片有 5 個元素,長度和容量均為 5。
- 當您使用
subslice := original[1:4]
時,它指向從索引 1 到 3 的元素 (2, 3, 4)。 -
subslice
的長度是 4 - 1 = 3。 -
subslice
的容量是 5 - 1 = 4,因為它從索引 1 開始,並包含到原始切片末尾的元素。
append()
陷阱!
這就是開發者經常被困住的地方。 Go語言中的append()
函數在處理子切片時可能會導致意外行為。
未使用的容量共享
子切片的容量包括不屬於其長度但位於原始切片容量範圍內的元素。這意味著如果子切片增長,它可以訪問或修改這些元素。
讓我們考慮這個例子:
package main import "fmt" func main() { // 创建一个具有初始值的切片 original := []int{1, 2, 3, 4, 5} // 创建一个子切片——两个切片共享相同的底层数组! subslice := original[1:3] fmt.Println("未修改的子切片:", subslice) // 输出 => 未修改的子切片: [2 3] // 修改子切片 subslice[0] = 42 fmt.Println("原始切片:", original) // 输出 => 原始切片: [1 42 3 4 5] fmt.Println("修改后的子切片:", subslice) // 输出 => 修改后的子切片: [42 3] }
-
subslice
最初指向 2, 3,容量為 4(它可以增長到原始切片的末尾)。 - 當您向
subslice
追加 60, 70 時,它使用原始切片的剩餘容量。 -
original
和subslice
都反映了這些更改,因為它們共享相同的底層數組。
驚訝嗎? append()
操作修改了原始切片,因為底層數組中有足夠的容量。但是,如果我們超過容量或追加的元素超過容量允許的範圍,Go將為子切片分配一個新的數組,從而打破與原始切片的共享:
func main() { // 原始切片 original := []int{1, 2, 3, 4, 5} // 创建一个子切片 subslice := original[1:4] // 指向元素 2, 3, 4 fmt.Println("子切片:", subslice) // 输出 => 子切片: [2 3 4] fmt.Println("子切片的长度:", len(subslice)) // 输出 => 子切片的长度: 3 fmt.Println("子切片的容量:", cap(subslice)) // 输出 => 子切片的容量: 4 }
在這種情況下,append()
創建了一個新的底層數組,因為原始容量已超過。
避免陷阱的最佳實踐
- 明確容量
func main() { original := []int{1, 2, 3, 4, 5} subslice := original[1:3] // 指向元素 2, 3 fmt.Println("追加前原始切片:", original) // 输出 => [1 2 3 4 5] fmt.Println("追加前子切片:", subslice) // 输出 => [2 3] fmt.Println("子切片的容量:", cap(subslice)) // 输出 => 4 // 在容量范围内追加到子切片 subslice = append(subslice, 60, 70) // 追加到子切片后打印 fmt.Println("追加后原始切片:", original) // 输出 => [1 2 3 60 70] fmt.Println("追加后子切片:", subslice) // 输出 => [2 3 60 70] }
主要優點是:
i. make([]int, len(subslice))
創建一個具有其自身獨立底層數組的新切片。這至關重要——它不僅僅是一個新的切片頭,而是在內存中一個全新的數組。
ii. copy()
然後只傳輸值,而不是內存引用。這就像複印一份文件而不是共享原始文件。
- 使用完整的切片表達式
func main() { original := []int{1, 2, 3, 4, 5} subslice := original[1:3] // 指向元素 2, 3 // 追加超出子切片容量的元素 subslice = append(subslice, 60, 70, 80) fmt.Println("大容量追加后原始切片:", original) // 输出 => [1 2 3 4 5] fmt.Println("大容量追加后子切片:", subslice) // 输出 => [2 3 60 70 80] }
- 在將切片傳遞給不應修改原始數據的函數時,考慮不變性
// 假设我们从这里开始 original := []int{1, 2, 3, 4, 5} subslice := original[1:3] // subslice 指向 original 的底层数组 // 这是我们的解决方案: newSlice := make([]int, len(subslice)) // 步骤 1:创建新的底层数组 copy(newSlice, subslice) // 步骤 2:复制值
主要優點是:
i. 數據保護:原始數據保持不變,防止意外副作用
ii. 可預測的行為:函數對輸入沒有隱藏的影響
iii. 並發安全:在處理過程中可以在其他 goroutine 中安全地使用原始數據
記住:
- 切片是對底層數組的引用
- 子切片與父切片共享內存
-
append()
是否創建新的底層數組取決於容量 - 當向具有可用容量的子切片追加元素時,它會修改父切片的數據。
- 當您想要避免共享時,請使用顯式內存管理
- 當處理子切片時,請執行以下任一操作:
type slice struct { array unsafe.Pointer // 指向底层数组的指针 len int // 切片中的元素数量 cap int // 底层数组的容量 }
祝您編碼愉快。記住,能力越大,責任越大,尤其是在共享內存方面! ?
恭喜您閱讀完本文。
您覺得這篇資源有幫助嗎?您有問題嗎?或者您發現了錯誤或錯別字?請在評論中留下您的反饋。
不要忘記與可能從中受益的其他人分享此資源。關注我以獲取更多信息。
以上是進行切片和子鏈條:了解共享內存並避免```append''的詳細內容。更多資訊請關注PHP中文網其他相關文章!

whentestinggocodewithinitfunctions,useexplicitseTupfunctionsorseParateTestFileSteSteTepteTementDippedDependendendencyOnInItfunctionsIdeFunctionSideFunctionsEffect.1)useexplicitsetupfunctionStocontrolglobalvaribalization.2)createSepEpontrolglobalvarialization

go'serrorhandlingurturnserrorsasvalues,與Javaandpythonwhichuseexceptions.1)go'smethodensursexplitirorhanderling,propertingrobustcodebutincreasingverbosity.2)

AnefactiveInterfaceingoisminimal,clear and promotesloosecoupling.1)minimizeTheInterfaceForflexibility andeaseofimplementation.2)useInterInterfaceForabStractionToswaPimplementations withoutchangingCallingCode.3)

集中式錯誤處理在Go語言中可以提升代碼的可讀性和可維護性。其實現方式和優勢包括:1.將錯誤處理邏輯從業務邏輯中分離,簡化代碼。 2.通過集中處理錯誤,確保錯誤處理的一致性。 3.使用defer和recover來捕獲和處理panic,增強程序健壯性。

Ingo,替代詞InivestoIniTfunctionsIncludeCustomInitializationfunctionsandsingletons.1)customInitializationfunctions hownerexpliticpliticpliticconconconconconconconconconconconconconconconconconconconconconconconconconconconconconconconconconconconconconconconconconconconconconconconconconconconconcontirization curssetupssetupssetups.2)單次固定無元素限制ininconconcurrent

Gohandlesinterfacesandtypeassertionseffectively,enhancingcodeflexibilityandrobustness.1)Typeassertionsallowruntimetypechecking,asseenwiththeShapeinterfaceandCircletype.2)Typeswitcheshandlemultipletypesefficiently,usefulforvariousshapesimplementingthe

Go語言的錯誤處理通過errors.Is和errors.As函數變得更加靈活和可讀。 1.errors.Is用於檢查錯誤是否與指定錯誤相同,適用於錯誤鏈的處理。 2.errors.As不僅能檢查錯誤類型,還能將錯誤轉換為具體類型,方便提取錯誤信息。使用這些函數可以簡化錯誤處理邏輯,但需注意錯誤鏈的正確傳遞和避免過度依賴以防代碼複雜化。

tomakegoapplicationsRunfasterandMorefly,useProflingTools,leverageConCurrency,andManageMoryfectily.1)usepprofforcpuorforcpuandmemoryproflingtoidentifybottlenecks.2)upitizegorizegoroutizegoroutinesandchannelstoparalletaparelalyizetasksandimproverperformance.3)


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

SublimeText3 英文版
推薦:為Win版本,支援程式碼提示!

Safe Exam Browser
Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。

禪工作室 13.0.1
強大的PHP整合開發環境

Atom編輯器mac版下載
最受歡迎的的開源編輯器

VSCode Windows 64位元 下載
微軟推出的免費、功能強大的一款IDE編輯器