【Go系列】Go的指針

承上啟下

? ? ? ? 我們在前面的文章中,首先介紹了GO的基礎語法,然后介紹了Goroutine和channel這個最具有特色的東西,同時介紹了Sync和context,以及在上篇文章中詳細距離說明了Go里面用于高并發的多種寫法。基礎的使用方法也告一段落了,我們要進入新的篇章,就是Go的指針,這邊的指針類型不僅是一個地址,還有Unsafe.Point,還有intptr,讓我們詳細看看。

開始學習

普通指針

在Go語言中,指針是一種特殊類型的變量,它存儲了另一個變量的內存地址。指針在Go中雖然不像C或C++那樣普遍使用,但它們在某些情況下仍然非常有用,尤其是在需要修改函數內部變量的值、避免大對象復制、實現數據結構(如鏈表、樹等)時。

以下是關于Go語言中指針的詳細介紹:

指針的基本概念

  • 內存地址:每個變量在內存中都有一個地址,指針變量存儲的就是這個地址。
  • 解引用:通過指針訪問它所指向的變量的值稱為解引用。

聲明指針

在Go中,指針的聲明方式是在變量類型前加上*

var pointer *int

這里,pointer是一個指向int類型變量的指針。

初始化指針

指針必須在使用前進行初始化。你可以使用&操作符來獲取一個變量的地址,并將其賦值給指針:

value := 10
pointer := &value

在這個例子中,pointer存儲了變量value的內存地址。

解引用指針

使用*操作符可以解引用指針,訪問或修改它所指向的值:

*pointer = 20

這將把變量value的值修改為20。

函數中的指針

在函數中傳遞指針允許你修改函數外部的變量:

func modifyValue(ptr *int) {*ptr = 30
}func main() {value := 10modifyValue(&value)fmt.Println(value) // 輸出 30
}

在這個例子中,modifyValue函數通過指針參數修改了外部變量value的值。

指針的nil值

一個未初始化的指針有一個nil值,表示它不指向任何地址:

var pointer *int
if pointer == nil {fmt.Println("Pointer is nil")
}

指針和結構體

指針常用于結構體,可以創建結構體的指針,并通過指針訪問或修改結構體的字段:

type Person struct {Name stringAge  int
}func main() {person := &Person{Name: "Alice", Age: 30}person.Age = 31 // 通過指針修改結構體的字段
}

指針數組與數組指針

  • 指針數組:一個數組,其元素是指針。

    var ptrArray [3]*int
    
  • 數組指針:一個指向數組的指針。

    var array [3]int
    var ptrToArray *[3]int = &array
    

指針的指針

雖然不常見,但你可以在Go中創建指向指針的指針:

var value int = 100
var ptr *int = &value
var ptrToPtr **int = &ptr

這里,ptrToPtr是一個指向ptr指針的指針。

注意事項

  • Go不支持指針算術,即你不能對指針進行加減操作。
  • Go的垃圾回收機制會自動管理內存,因此通常不需要手動釋放指針指向的內存。

Unsafe.Point

在Go語言中,unsafe.Pointer?是一個特殊類型的指針,它可以指向任意類型的值。unsafe?包提供了一些繞過Go類型系統的功能,允許程序進行一些原本不被允許的操作,比如在不同指針類型之間進行轉換,或者計算一個對象的實際內存大小等。

以下是關于?unsafe.Pointer?的一些關鍵點:

類型轉換

unsafe.Pointer?可以用于在任意指針類型之間進行轉換。例如,如果你有一個?*int?類型的指針,你可以將其轉換為?unsafe.Pointer,然后再轉換回其他類型的指針。

package mainimport ("fmt""unsafe"
)func main() {i := 42ip := &i         // *intptr := unsafe.Pointer(ip) // 轉換為 unsafe.Pointeruptr := uintptr(ptr)     // 轉換為 uintptr// 反向轉換ptr = unsafe.Pointer(uptr) // 轉換回 unsafe.Pointerip2 := (*int)(ptr)        // 轉換回 *int*ip2 = 84fmt.Println(i) // 輸出 84
}

訪問任意內存地址

通過?unsafe.Pointer,你可以訪問任意內存地址,這在Go的常規操作中是不被允許的,因為它繞過了Go的類型系統和內存安全檢查。

ptr := unsafe.Pointer(uintptr(0x12345678))

上述代碼試圖訪問一個特定的內存地址,這在實際的程序中是非常危險的,因為它可能導致未定義行為,包括程序崩潰。

計算結構體大小

unsafe.Sizeof?函數可以返回一個值的大小,單位是字節。這個函數通常與?unsafe.Pointer?一起使用來計算結構體的大小。

type MyStruct struct {a intb string
}s := MyStruct{a: 1, b: "hello"}
size := unsafe.Sizeof(s)
fmt.Println(size) // 輸出結構體 MyStruct 的大小

使用?uintptr?進行指針算術

雖然Go不支持?unsafe.Pointer?的算術操作,但你可以將?unsafe.Pointer?轉換為?uintptr,然后在?uintptr?上執行算術操作,最后再轉換回?unsafe.Pointer

ptr := unsafe.Pointer(&s)
uptr := uintptr(ptr)
newPtr := unsafe.Pointer(uptr + unsafe.Offsetof(s.b)) // 訪問結構體中的 b 字段

注意事項

  • 使用?unsafe?包繞過Go的類型系統和內存安全機制,需要非常小心,因為錯誤的使用可能會導致程序崩潰或者安全漏洞。
  • unsafe.Pointer?的使用應該限制在必要的范圍內,并且要確保操作的安全性。
  • unsafe?包的內容可能會在不同的Go版本之間發生變化,因此在使用時應保持謹慎。

由于?unsafe?包的功能非常強大,Go官方建議開發者只有在沒有其他選擇的情況下才使用它,并且要確保代碼的穩定性和安全性。

UintPtr

在Go語言中,intptr?并不是一個內置的類型。你可能在提到?uintptr?時出現了誤解,或者是在引用其他語言中的類型。在Go語言中,與指針操作相關的類型是?uintptr

uintptr?類型

uintptr?是?unsafe?包中的一個類型,它足夠大,可以存儲任何類型的指針的位模式(即內存地址)。uintptr?類型主要用于低級編程,比如與操作系統接口、內存操作等。

以下是一些關于?uintptr?的關鍵點:

  • uintptr?是一個無符號整數類型,其大小足以容納任何指針的位模式。
  • 它可以用于將指針轉換為整數,反之亦然。
  • uintptr?可以用于執行指針算術,但這樣做需要非常小心,因為它可能會繞過Go的內存安全保證。

示例

以下是如何使用?uintptr?的示例:

package mainimport ("fmt""unsafe"
)func main() {i := 42ptr := &i         // *intuptr := uintptr(unsafe.Pointer(ptr)) // 轉換為 uintptr// 使用 uintptr 進行指針算術newPtr := unsafe.Pointer(uptr + unsafe.Sizeof(i))// 反向轉換回指針類型newIntPtr := (*int)(newPtr)*newIntPtr = 84fmt.Println(i) // 輸出 84
}

在這個例子中,我們首先將一個?*int?類型的指針轉換為?uintptr,然后執行了指針算術操作(雖然在這個特定的例子中這樣做沒有意義,因為它只是在一個整數大小的范圍內移動),最后將結果轉換回?*int?類型的指針。

注意事項

  • 使用?uintptr?需要非常小心,因為不正確的使用可能會導致內存安全問題,比如訪問未分配的內存、越界訪問等。
  • uintptr?類型的值不應該被存儲或以任何方式保留,因為它們可能會在垃圾回收期間變得無效。
  • 通常情況下,Go程序員不需要直接使用?uintptr,除非他們正在編寫需要直接與操作系統或硬件交互的底層代碼。

三種指針類型對比

在Go語言中,指針、unsafe.Pointer?和?uintptr?是三種不同的概念,它們在內存操作和類型轉換中扮演著不同的角色。下面是它們的區別:

指針(如?*int

  • 定義:指針是一種變量,它存儲了另一個變量的內存地址。
  • 用途:用于引用和修改變量,或者在函數調用中傳遞變量的地址以修改其值。
  • 類型安全:指針是類型安全的,它們只能指向特定類型的變量。
  • 示例
    var a int = 42
    var ptr *int = &a
    *ptr = 100 // 修改a的值
    

unsafe.Pointer

  • 定義unsafe.Pointer?是?unsafe?包中的一個特殊類型,它可以指向任意類型的變量。
  • 用途:用于在不同指針類型之間進行轉換,或者在需要時進行底層的內存操作。
  • 類型安全unsafe.Pointer?本身不是類型安全的,因為它可以指向任何類型的變量,但它需要與其他類型安全的指針一起使用。
  • 示例
    var a int = 42
    var ptr unsafe.Pointer = unsafe.Pointer(&a)
    

uintptr

  • 定義uintptr?是?unsafe?包中的一個無符號整數類型,其大小足以存儲任何類型的指針的位模式。
  • 用途:用于執行指針算術操作,或者將指針轉換為整數以便進行低級內存操作。
  • 類型安全uintptr?不是類型安全的,因為它可以存儲任何指針的位模式,并且可以用于執行指針算術,這可能會繞過Go的內存安全保證。
  • 示例
    var a int = 42
    var ptr uintptr = uintptr(unsafe.Pointer(&a))
    

區別

  • 類型安全

    • 指針是類型安全的,只能用于指向特定類型的變量。
    • unsafe.Pointer?不是類型安全的,可以指向任何類型的變量,但它需要與其他類型安全的指針一起使用。
    • uintptr?也不是類型安全的,它可以存儲任何指針的位模式,并且可以用于執行指針算術。
  • 用途

    • 指針主要用于變量引用和修改變量的值。
    • unsafe.Pointer?用于在不同指針類型之間進行轉換,或者在需要時進行底層的內存操作。
    • uintptr?用于執行指針算術操作,或者將指針轉換為整數以便進行低級內存操作。
  • 內存安全

    • 使用指針時,Go的垃圾回收器會確保指向的變量在需要時不會被回收。
    • 使用?unsafe.Pointer?和?uintptr?時,程序員需要確保操作不會導致內存安全問題,比如越界訪問或訪問未分配的內存。

總結來說,指針是Go中用于日常變量引用和修改變量的類型安全工具,而?unsafe.Pointer?和?uintptr?用于更底層的內存操作,它們提供了更大的靈活性和能力,但同時也帶來了更高的風險,因為它們不是類型安全的,并且需要程序員更加小心地使用。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/45297.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/45297.shtml
英文地址,請注明出處:http://en.pswp.cn/web/45297.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Linux多線程編程-哲學家就餐問題詳解與實現(C語言)

在哲學家就餐問題中,假設有五位哲學家圍坐在圓桌前,每位哲學家需要進行思考和進餐兩種活動。他們的思考不需要任何資源,但進餐需要使用兩根筷子(左右兩側各一根)。筷子是共享資源,哲學家們在進行進餐時需要…

Qt qml詳細介紹

一.基本類型 QML的基本類型包括了很多不同的類型,這些類型可以用于定義用戶界面元素、屬性和信號。以下是一些常用的QML基本類型及其詳細介紹: 數值類型:包括整數類型(int、uint、short、ushort等)和浮點數類型&#…

c++ :運算符重載函數中的細節

賦值運算符重載與拷貝構造函數 (1)區分初始化時的賦值(一般就叫初始化),和非初始化時的賦值(一般就叫賦值) (2)實驗驗證初始化和賦值時各自對應 避免賦值運算符中的自賦值 (1)自賦值就是Person a; a a; (2)自賦值如…

鞭炮插畫:成都亞恒豐創教育科技有限公司

鞭炮插畫:年味里的絢爛記憶 在歲末年初的溫柔時光里,總有一抹色彩,能瞬間喚醒沉睡的年味——那便是鞭炮插畫中躍動的紅與金,成都亞恒豐創教育科技有限公司 它們不僅僅是紙與墨的交織,更是情感與記憶的橋梁&#xff0c…

自適應手機版大學職業技術學院網站模版源碼系統 帶完整的安裝代碼包以及搭建部署教程

系統概述 隨著智能手機的普及和移動互聯網技術的飛速發展,用戶越來越傾向于通過移動設備訪問網站。對于大學職業技術學院而言,一個能夠自適應各種屏幕尺寸、操作流暢、內容豐富的移動端網站,不僅能夠提升用戶體驗,還能有效擴大學…

最短路之樸素版的dij板子

模板&#xff1a; 注意這個只是單向的雙向的需要在更新一次 #include<bits/stdc.h>using namespace std;typedef long long ll; typedef pair<int, int>PII; const int N2e510; const int MOD 998244353; const int INF0X3F3F3F3F; const int dx[]{-1,1,0,0,-1,…

【Python Tips】將一個列表List元素添加進另一個列表List

一、引言 在處理Python列表數據類型時&#xff0c;有時需要合并兩個列表&#xff0c;下面是幾種列表合并的操作代碼&#xff0c;尤其是對于長列表的高效合并方式&#xff0c;記錄在此。 二、列表合并方式 1. 使用extend方法 extend方法將一個列表中的所有元素添加到另一個列表…

mysql快速精通(三)表關系

主打一個實用 一. 一對多&#xff08;多對一&#xff09;關系 例如班級和學生&#xff0c;這種類型我們一般建兩個表,一方為主表&#xff0c;多方為從表 二. 多對多 例如課程與學生&#xff0c;這種類型我們一般需要建三張表&#xff0c;兩張一方主表&#xff0c;與一張多方從表…

初識影刀:EXCEL根據部門篩選低值易耗品

第一次知道這個辦公自動化的軟件還是在招聘網站上&#xff0c;了解之后發現對于辦公中重復性的工作還是挺有幫助的&#xff0c;特別是那些操作非EXCEL的重復性工作&#xff0c;當然用在EXCEL上更加方便&#xff0c;有些操作比寫VBA便捷。 下面就是一個了解基本操作后&#xff…

[Linux]CentOS軟件的安裝

一、Linux 軟件包管理器 yum 1.Linux安裝軟件的方式 在linux中安裝軟件常用的有三種方式&#xff1a; 源代碼安裝&#xff08;我們還需要進行編譯運行后才可以&#xff0c;很麻煩&#xff09; rpm安裝&#xff08;Linux的安裝包&#xff0c;需要下載一些rpm包&#xff0c;但是…

基于機器學習的鋰離子電池容量估計(MATLAB R2021B)

鋰離子電池已經廣泛應用于電動汽車或混合動力汽車的能源存儲裝置。由于電化學成分的衰退&#xff0c;鋰離子電池隨著使用時間的增加&#xff0c;電池性能不斷退化&#xff0c;導致電池容量和功率發生衰退。電池容量衰退的因素主要有金屬鋰沉積&#xff0c;活性物質分解和電解液…

深度學習DeepLearning多元線性回歸 學習筆記

文章目錄 多維特征變量與術語公式多元線性回歸正規方程法Mean normalizationZ-score normalization設置合適的學習率Feature engineering 多維特征 變量與術語 列屬性xj屬性數n x ? \vec{x} x (i)行向量某個值 x ? j i \vec{x}_j^i x ji?上行下列均值μ標準化標準差σsigm…

SpringMVC 中常用注解

在 SpringMVC 框架的開發中&#xff0c;注解的合理運用能夠極大地提高開發效率和代碼的可維護性。今天&#xff0c;讓我們一起來總結一下 SpringMVC 中一些常用的注解及其用法。 一、Controller 注解 Controller 用于標識一個控制器類&#xff0c;該類中的方法用于處理用戶的請…

ArduPilot開源代碼之AP_AHRS_Backend

ArduPilot開源代碼之AP_AHRS_Backend 1. 源由2. 類繼承關系3. 框架設計2.1 構造函數和析構函數2.2 不可復制2.3 嵌套結構和枚舉2.4 虛方法2.5 靜態方法2.6 實用方法2.7 純虛方法2.8 條件編譯 3. 虛方法設計3.1 初始化3.1.1 構造函數3.1.2 析構函數3.1.3 AP_AHRS_Backend::init …

Chromium CI/CD 之Jenkins實用指南2024-如何創建新節點(三)

1. 前言 在前一篇《Jenkins實用指南2024-系統基本配置&#xff08;二&#xff09;》中&#xff0c;我們詳細介紹了如何對Jenkins進行基本配置&#xff0c;包括系統設置、安全配置、插件管理以及創建第一個Job。通過這些配置&#xff0c;您的Jenkins環境已經具備了基本的功能和…

基于pyqt5實現xlsx選擇器應用程序

環境搭建 基于python3.12pyqt5 pip3 install PyQt5 pip3 install pyinstallerpyinstaller --onefile --windowed test.py代碼 新建main.py import sys from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLineEdit, QFileDial…

leetcode 665.非遞減數列

1.題目要求: 給你一個長度為 n 的整數數組 nums &#xff0c;請你判斷在 最多 改變 1 個元素的情況下&#xff0c;該數組能否變成一個非遞減數列。我們是這樣定義一個非遞減數列的&#xff1a; 對于數組中任意的 i (0 < i < n-2)&#xff0c;總滿足 nums[i] < nums[i…

Java 設計模式系列:外觀模式

簡介 外觀模式&#xff08;Facade Pattern&#xff09;是一種設計模式&#xff0c;又名門面模式&#xff0c;是一種通過為多個復雜的子系統提供一個一致的接口&#xff0c;而使這些子系統更加容易被訪問的模式。該模式對外有一個統一接口&#xff0c;外部應用程序不用關心內部…

Android中RecyclerView使用詳解(一)

目錄 概述優點列表布局RecyclerView一、創建RecyclerView并且在布局中綁定二、實現RecyclerView單個item的布局三、給RecyclerView寫一個對應的適配器Adapter1.創建自定義的ViewHolder2.繼承Adapter&#xff0c;泛型使用我們自定義的ViewHolder3.重寫Adapter的三個方法onCreate…

線程安全(二)synchronized 的底層實現原理、鎖升級、對象的內存結構

目錄 一、基礎使用1.1 不加鎖的代碼實現1.2 加鎖的代碼實現二、實現原理2.1 synchronized 簡介2.2 對象監控器(Monitor)2.3 加鎖過程第一步:判斷 Owner 指向第二步:進入 EntryList 阻塞第三步:主動進入 WaitSet 等待三、鎖升級3.1 對象的內存結構3.2 Mark Word 對象頭3.3 …