淺談Go中的Pointer和記憶體管理

閱讀時間約 9 分鐘


raw-image

👨‍💻簡介

當我們在宣告變數時,電腦會為該變數在記憶體中分配一個位置,然後將這個變數值儲存在這個位置上,需要讀取或修改這個變數值時,電腦是透過記憶體位置來存取這個值。 今天來簡單介紹一下go的Pointer,他的特性以及常見用法。

什麼是Pointer?

Pointer是一種資料類型,用來儲存變數的記憶體地址。在Go中,我們可以通過使用 * 符號來宣告和操作Pointer。這允許我們直接訪問和修改變數的內容,而不僅僅是讀取或複製它們的值。

Pointer的特性、限制與常見用法

特性

  • Pointer的值和地址

每個變數都有一個記憶體地址,我們可以使用Pointer變數來儲存這個地址。讓我們看一個範例:

package main

import "fmt"
func main() {
x := 42
var p *int // 宣告一個整數Pointer
p = &x // 將p指向x的地址
fmt.Println("x =", x)
fmt.Println("p =", p)
}

在這個例子中,我們創建了一個整數變數 x,並宣告了一個整數Pointer p,然後將 p 設為 x 的地址。現在,p 裡面存放的就是 x 的地址。

  • Pointer的初始化

Go中的Pointer可以通過 new 函數來初始化,這將為指定的類型分配記憶體並返回其地址。範例:

package main

import "fmt"
func main() {
var p *int
p = new(int) // 初始化一個整數Pointer
*p = 123 // 將Pointer所指向的記憶體設置為123
fmt.Println("*p =", *p)
}

可以看到,我們創建了一個整數Pointer p,並使用 new(int) 初始化它,然後將 p 所指向的記憶體設置為 123

  • Pointer的解引用

通過Pointer,我們可以訪問和修改變數的值,這稱為Pointer的解引用。

package main

import "fmt"
func main() {
x := 42
p := &x // 將p設為x的地址,使用&
fmt.Println("x =", x)
fmt.Println("&x =", &x)
fmt.Println("*p =", *p) // 解引用Pointer,獲得x的值,使用*
*p = 99 // 通過Pointer修改x的值
fmt.Println("x =", x) // x的值已被修改
fmt.Println("&x =", &x)
fmt.Println("*p =", *p)
}

在這個例子中,我們使用Pointer p 解引用,並修改了變數 x 的值。也可以觀察到修改值並不會改變記憶體位置。

限制

  1. 空Pointer風險:未初始化的Pointer可能為nil,試圖解引用nil Pointer會導致運行時錯誤(panic)。
  2. 記憶體管理:Go中的垃圾回收器會管理記憶體,但Pointer仍然需要謹慎使用,以避免記憶體錯誤。
  3. 競爭條件:多線程環境中,共享的Pointer可能導致競爭條件,需要使用互斥鎖等技術來保護共享資源。

常見用法

  • 節省記憶體

當需要處理大型資料結構時,使用Pointer可以節省記憶體,因為它們只儲存變數的地址,而不是整個資料。

package main

import (
"fmt"
"unsafe"
)
// 定義一個名為 Person 的結構(struct)
type Person struct {
Name string // 名稱
Age int // 年齡
}
func main() {
// 創建一個 Person 變數,名稱為 Alice,年齡為 30 歲
alice := Person{Name: "Alice", Age: 30}
// 計算 Person 變數 alice 的大小
personSize := unsafe.Sizeof(alice)
// 創建一個指向 Person 變數的Pointer,名稱為 Bob,年齡為 24 歲
bob := &Person{Name: "Bob", Age: 24}
// 計算指向 Person 變數 bob 的Pointer的大小
pointerSize := unsafe.Sizeof(bob)
fmt.Printf("alice -> Person 變數的大小: %d 個位元組\n", personSize)
fmt.Printf("bob -> 指向 Person 變數的Pointer的大小: %d 個位元組\n", pointerSize)
fmt.Println(&alice) // 列印 alice 變數的記憶體位置
fmt.Println(&bob) // 列印 bob 變數(Pointer)的記憶體位置
}

在上面的範例中,我們透過兩種方式創建person,但可以觀察到使用Pointer的方式創建,使用的大小較小,達到節省記憶體的作用。

  • 改變資料的原始值

想要在函數中修改變數的值,而不只是副本時,Pointer變得非常有用。

package main

import "fmt"
func modifyValue(x *int) {
*x = 100
}
func main() {
y := 10
modifyValue(&y) // 通過Pointer修改y的值
fmt.Println("y =", y)
}
  • 效能優勢

在某些情況下,使用Pointer可以提高性能,因為它們允許直接訪問和修改記憶體,而不需要額外的記憶體複製操作。

package main

import (
"fmt"
"time"
)
const N = 1000000 // 陣列大小
// 使用值傳遞的函數,不使用指標
func sumValues(arr [N]int) int {
sum := 0
for _, v := range arr {
sum += v
}
return sum
}
// 使用指標傳遞的函數,避免陣列複製
func sumPointers(arr *[N]int) int {
sum := 0
for _, v := range arr {
sum += v
}
return sum
}
func main() {
// 創建一個包含大量資料的整數陣列
var arr [N]int
for i := 0; i < N; i++ {
arr[i] = i
}
// 測試不使用指標的情況,複製陣列
startTime := time.Now()
result1 := sumValues(arr)
duration1 := time.Since(startTime)
// 測試使用指標的情況,避免複製陣列
startTime = time.Now()
result2 := sumPointers(&arr)
duration2 := time.Since(startTime)
fmt.Printf("不使用指標的結果:%d,執行時間:%v\n", result1, duration1)
fmt.Printf("使用指標的結果:%d,執行時間:%v\n", result2, duration2)
}

可以看到,我們創建了一個包含大量資料的整數陣列,並寫了兩個函數來計算陣列中所有元素的總和。一個函數 sumValues 使用值傳遞陣列,另一個函數 sumPointers 使用指標傳遞陣列。

在 main 函數中,測試了這兩個函數的效能。結果顯示,使用指標傳遞陣列可以明顯提高效能,因為它少了陣列的複製,節省了大量的時間和記憶體。

小結

Pointer是程式語言中的重要概念,它允許我們直接訪問和修改變數的內容,並在許多情況下提供性能優勢和節省記憶體的機會。但還是要小心使用Pointer,確保程式碼的穩定性和安全性。

📚Reference

avatar-img
17會員
83內容數
golang
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
Alan的開發者天地 的其他內容
👨‍💻簡介 在軟體開發中,錯誤無所不在。無論是網路請求失敗、檔案不存在,還是數學計算錯誤,處理錯誤是任何開發者的日常工作,系統的穩定度基本取決於對於錯誤處理是否全面,好的錯誤處理也可以產生適當的錯誤訊息,讓 Debug 更加容易。
👨‍💻簡介 當我們在寫程式時,有時候會需要在程式結束時關閉某些資源,而defer這個關鍵字,可以讓你輕鬆的實現,下面來簡單介紹一下defer以及常用的範例。,它為程式設計師提供了一種簡單而強大的工具,用於管理資源和確保程式的正確執行。
👨‍💻簡介 在Go中,假如我要判斷一個資料類型是甚麼,該怎麼做呢? Golang有一個功能叫做Type Assertions(類型斷言),它的作用就是能夠在運行時檢查我的資料類型,讓我在傳遞類型時能確保資料類型是正確的。
👨‍💻簡介 make函數在slice、map和之後會介紹到的channel的初始化中扮演著關鍵的角色。本文將會簡單介紹make函數的用法,以及在初始化不同資料結構時的差異,讓你更好地理解和利用make函數。
👨‍💻簡介 套件(Package)在Golang中扮演著組織和管理程式碼的重要角色。 package就像工具箱一樣,裡面裝滿各種不同的工具,每個工具都有特定的功能。這些工具能夠幫助你完成不同的任務,從修理家具到蓋小屋,樣樣都行。
👨‍💻簡介 在Go語言中,Interface 是一個重要且強大的概念。Interface提供了一種方式來定義對象之間的契約,讓你可以設計更具有靈活性和可擴展性的程式碼。 你可以把Interface想像成是一種約定,讓不同的東西彼此溝通的方式變得特別靈活和好擴充,告訴程式裡的各個元件彼此要怎麼合作
👨‍💻簡介 在軟體開發中,錯誤無所不在。無論是網路請求失敗、檔案不存在,還是數學計算錯誤,處理錯誤是任何開發者的日常工作,系統的穩定度基本取決於對於錯誤處理是否全面,好的錯誤處理也可以產生適當的錯誤訊息,讓 Debug 更加容易。
👨‍💻簡介 當我們在寫程式時,有時候會需要在程式結束時關閉某些資源,而defer這個關鍵字,可以讓你輕鬆的實現,下面來簡單介紹一下defer以及常用的範例。,它為程式設計師提供了一種簡單而強大的工具,用於管理資源和確保程式的正確執行。
👨‍💻簡介 在Go中,假如我要判斷一個資料類型是甚麼,該怎麼做呢? Golang有一個功能叫做Type Assertions(類型斷言),它的作用就是能夠在運行時檢查我的資料類型,讓我在傳遞類型時能確保資料類型是正確的。
👨‍💻簡介 make函數在slice、map和之後會介紹到的channel的初始化中扮演著關鍵的角色。本文將會簡單介紹make函數的用法,以及在初始化不同資料結構時的差異,讓你更好地理解和利用make函數。
👨‍💻簡介 套件(Package)在Golang中扮演著組織和管理程式碼的重要角色。 package就像工具箱一樣,裡面裝滿各種不同的工具,每個工具都有特定的功能。這些工具能夠幫助你完成不同的任務,從修理家具到蓋小屋,樣樣都行。
👨‍💻簡介 在Go語言中,Interface 是一個重要且強大的概念。Interface提供了一種方式來定義對象之間的契約,讓你可以設計更具有靈活性和可擴展性的程式碼。 你可以把Interface想像成是一種約定,讓不同的東西彼此溝通的方式變得特別靈活和好擴充,告訴程式裡的各個元件彼此要怎麼合作
你可能也想看
Google News 追蹤
Thumbnail
嘿,大家新年快樂~ 新年大家都在做什麼呢? 跨年夜的我趕工製作某個外包設計案,在工作告一段落時趕上倒數。 然後和兩個小孩過了一個忙亂的元旦。在深夜時刻,看到朋友傳來的解籤網站,興致勃勃熬夜體驗了一下,覺得非常好玩,或許有人玩過了,但還是想寫上來分享紀錄一下~
Thumbnail
就如同標題一樣,input的作用就是從使用者那裡獲取輸入,直到使用者輸入一段文本並按下 ENTER 鍵。 然而用戶輸入的數據(文本)都將作為字串被返回,並存儲在變數中。 接著我們舉個例,比如說我們在一段數據中需要獲取使用者的名稱,範例如下: name = input("請輸入你的名字:") #
Thumbnail
我們在上一篇簡單介紹了 int(整數)是做什麼用的,接下來要介紹常和他一起出現的好朋友 float 浮點數 跟 str 字串。 float 浮點數: 函數的式子寫做 float( ) ,浮點數就是帶有小數點的資料型別,他可以將字串或是數字轉換為有小數點的狀態。前提是字串內的字符必須是數字的格
Thumbnail
今天要來介紹的是Python中資料型別的函數, 這幾天學習的素材是Youtube上“程式柴大大的Python 6 小時初學者課程”,一步一步帶著大家操作並解,學習中也別忘了要多多練習,練習的部分我是把我學到的東西請Chatgpt幫我出類似的題型並讓我練習。 以下我先寫出一個簡單的code,再加以
Thumbnail
這篇內容,將會講解什麼是變數範圍,以及與變數範圍相關的知識。包括變數範圍的簡介、實體變數、全域變數、局部變數、常數。
Thumbnail
這篇內容,將會講解什麼是變數,以及與變數相關的知識。包括變數、資料型態、變數賦值、變數的命名規則、變數的作用區域、變數的可重複性、內建變數。
Thumbnail
本文詳細介紹了Python中的各種資料型別,包括整數、字串、清單、元組、集合和字典,並提供了相關的操作範例。此外,還解釋了如何在Python中定義和操作變數,包括如何同時對多個變數進行賦值。
※ 常用Object(物件)型態的方法: 拿到object裡面某個key的value(值): 拿到所有屬性: ※ 存取物件屬性:點記法與括號記法 使用點記法 (dot notation) 或括號記法 (bracket notation) 來存取物件的值,以下兩種寫法的結果是一樣的: //
※ 認識變數: 變數(variable)是在程式裡面把東西存起來的概念。 基於不讓電腦每次都需要重新運算,把需要花時間運算的東西先存起來,之後再直接拿出來使用,所以我們需要變數。 變數儲存的位置在電腦裡的記憶體。變數就像是一個箱子,記憶體就像是一個倉庫。箱子的名稱就是變數名稱,箱子的內容物就是
Thumbnail
宣告變數 變數是程式中用來儲存和表示數據的標識符號​,並將變數存放在某個記憶體位子 可以用ID的方法查找變數存在哪個記憶體,此方法有利於以後查找問題用。 在大多數程式語言中,變數需要事先聲明(宣告)並賦值。 而Python是一種動態類型語言,不需要顯式宣告變數類型,而是在賦值時自動進行推斷。
Thumbnail
嘿,大家新年快樂~ 新年大家都在做什麼呢? 跨年夜的我趕工製作某個外包設計案,在工作告一段落時趕上倒數。 然後和兩個小孩過了一個忙亂的元旦。在深夜時刻,看到朋友傳來的解籤網站,興致勃勃熬夜體驗了一下,覺得非常好玩,或許有人玩過了,但還是想寫上來分享紀錄一下~
Thumbnail
就如同標題一樣,input的作用就是從使用者那裡獲取輸入,直到使用者輸入一段文本並按下 ENTER 鍵。 然而用戶輸入的數據(文本)都將作為字串被返回,並存儲在變數中。 接著我們舉個例,比如說我們在一段數據中需要獲取使用者的名稱,範例如下: name = input("請輸入你的名字:") #
Thumbnail
我們在上一篇簡單介紹了 int(整數)是做什麼用的,接下來要介紹常和他一起出現的好朋友 float 浮點數 跟 str 字串。 float 浮點數: 函數的式子寫做 float( ) ,浮點數就是帶有小數點的資料型別,他可以將字串或是數字轉換為有小數點的狀態。前提是字串內的字符必須是數字的格
Thumbnail
今天要來介紹的是Python中資料型別的函數, 這幾天學習的素材是Youtube上“程式柴大大的Python 6 小時初學者課程”,一步一步帶著大家操作並解,學習中也別忘了要多多練習,練習的部分我是把我學到的東西請Chatgpt幫我出類似的題型並讓我練習。 以下我先寫出一個簡單的code,再加以
Thumbnail
這篇內容,將會講解什麼是變數範圍,以及與變數範圍相關的知識。包括變數範圍的簡介、實體變數、全域變數、局部變數、常數。
Thumbnail
這篇內容,將會講解什麼是變數,以及與變數相關的知識。包括變數、資料型態、變數賦值、變數的命名規則、變數的作用區域、變數的可重複性、內建變數。
Thumbnail
本文詳細介紹了Python中的各種資料型別,包括整數、字串、清單、元組、集合和字典,並提供了相關的操作範例。此外,還解釋了如何在Python中定義和操作變數,包括如何同時對多個變數進行賦值。
※ 常用Object(物件)型態的方法: 拿到object裡面某個key的value(值): 拿到所有屬性: ※ 存取物件屬性:點記法與括號記法 使用點記法 (dot notation) 或括號記法 (bracket notation) 來存取物件的值,以下兩種寫法的結果是一樣的: //
※ 認識變數: 變數(variable)是在程式裡面把東西存起來的概念。 基於不讓電腦每次都需要重新運算,把需要花時間運算的東西先存起來,之後再直接拿出來使用,所以我們需要變數。 變數儲存的位置在電腦裡的記憶體。變數就像是一個箱子,記憶體就像是一個倉庫。箱子的名稱就是變數名稱,箱子的內容物就是
Thumbnail
宣告變數 變數是程式中用來儲存和表示數據的標識符號​,並將變數存放在某個記憶體位子 可以用ID的方法查找變數存在哪個記憶體,此方法有利於以後查找問題用。 在大多數程式語言中,變數需要事先聲明(宣告)並賦值。 而Python是一種動態類型語言,不需要顯式宣告變數類型,而是在賦值時自動進行推斷。