為什麼 Go for-range 的 value 值地址每次都一樣?

来源:https://www.cnblogs.com/alwaysbeta/archive/2023/04/30/17365315.html
-Advertisement-
Play Games

原文鏈接: 為什麼 Go for-range 的 value 值地址每次都一樣? 迴圈語句是一種常用的控制結構,在 Go 語言中,除了 for 關鍵字以外,還有一個 range 關鍵字,可以使用 for-range 迴圈迭代數組、切片、字元串、map 和 channel 這些數據類型。 但是在使用 ...


原文鏈接: 為什麼 Go for-range 的 value 值地址每次都一樣?

迴圈語句是一種常用的控制結構,在 Go 語言中,除了 for 關鍵字以外,還有一個 range 關鍵字,可以使用 for-range 迴圈迭代數組、切片、字元串、map 和 channel 這些數據類型。

但是在使用 for-range 迴圈迭代數組和切片的時候,是很容易出錯的,甚至很多老司機一不小心都會在這裡翻車。

具體是怎麼翻的呢?我們接著看。

現象

先來看兩段很有意思的代碼:

無限迴圈

如果我們在遍曆數組的同時向數組中添加元素,能否得到一個永遠都不會停止的迴圈呢?

比如下麵這段代碼:

func main() {
    arr := []int{1, 2, 3}
    for _, v := range arr {
        arr = append(arr, v)
    }
    fmt.Println(arr)
}

程式輸出:

$ go run main.go
1 2 3 1 2 3

上述代碼的輸出意味著迴圈只遍歷了原始切片中的三個元素,我們在遍歷切片時追加的元素並沒有增加迴圈的執行次數,所以迴圈最終還是停了下來。

相同地址

第二個例子是使用 Go 語言經常會犯的一個錯誤。

當我們在遍歷一個數組時,如果獲取 range 返回變數的地址並保存到另一個數組或者哈希時,會遇到令人困惑的現象:

func main() {
    arr := []int{1, 2, 3}
    newArr := []*int{}
    for _, v := range arr {
        newArr = append(newArr, &v)
    }
    for _, v := range newArr {
        fmt.Println(*v)
    }
}

程式輸出:

$ go run main.go
3 3 3

上述代碼並沒有輸出 1 2 3,而是輸出 3 3 3

正確的做法應該是使用 &arr[i] 替代 &v,像這種編程中的細節是很容易出錯的。

原因

具體原因也並不複雜,一句話就能解釋。

對於數組、切片或字元串,每次迭代,for-range 語句都會將原始值的副本傳遞給迭代變數,而非原始值本身。

口說無憑,具體是不是這樣,還得靠源碼說話。

Go 編譯器會將 for-range 語句轉換成類似 C 語言的三段式迴圈結構,就像這樣:

// Arrange to do a loop appropriate for the type.  We will produce
//   for INIT ; COND ; POST {
//           ITER_INIT
//           INDEX = INDEX_TEMP
//           VALUE = VALUE_TEMP // If there is a value
//           original statements
//   }

迭代數組時,是這樣:

// The loop we generate:
//   len_temp := len(range)
//   range_temp := range
//   for index_temp = 0; index_temp < len_temp; index_temp++ {
//           value_temp = range_temp[index_temp]
//           index = index_temp
//           value = value_temp
//           original body
//   }

切片

//   for_temp := range
//   len_temp := len(for_temp)
//   for index_temp = 0; index_temp < len_temp; index_temp++ {
//           value_temp = for_temp[index_temp]
//           index = index_temp
//           value = value_temp
//           original body
//   }

從上面的代碼片段,可以總結兩點:

  1. 在迴圈開始前,會將數組或切片賦值給一個新變數,在賦值過程中就發生了拷貝,迭代的實際上是副本,這也就解釋了現象 1。
  2. 在迴圈過程中,會將迭代元素賦值給一個臨時變數,這又發生了拷貝。如果取地址的話,每次都是一樣的,都是臨時變數的地址。

以上就是本文的全部內容,如果覺得還不錯的話歡迎點贊轉發關註,感謝支持。


參考文章:

推薦閱讀:


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前言 本篇文章主要介紹的關於本人在使用MySql記錄筆記的一些使用方法和經驗,溫馨提示,本文有點長,約1.5w字,幾十張圖片,建議收藏查看。 一、MySql安裝 下載地址:https://dev.mysql.com/downloads/ 在安裝MySql之前,查看是否以及安裝過MySql,如果已經安 ...
  • CSS知識點總結 文章內容可能較多且雜亂,可以查看頁面右方的目錄,以及使用Ctrl+F搜索頁面內容進行內容定位。 常用屬性 推薦搭配文檔使用,可以複製屬性名,到文檔查看該屬性對應的可選值。 👉MDN Web Docs 盒模型 寬度:width 高度:height 邊框:border 圓角:bord ...
  • 1、四層結構 viewer --> datasources(DataSourceCollection類型) --> datasource --> entities(EntityCollection類型) --> entity 需要學習的方向是:只需要註意每個層與層之間的關係和entity實例如何創建 ...
  • 一、前言 第二次在博客園上發佈面向對象程式設計題目集的總結博客。經過幾周的學習,面向對象的理念更加深入。雖然已經學了些面向對象程式設計,學好這部分內容還是有較大難度。 關於知識點 本次的題目集所體現的知識點已經不僅限於Java的語法知識,還需要考慮設計問題,不能看到題目就開始進行代碼編寫,需要考慮類 ...
  • B/S 結構系統的 緩存機制(Cookie) 以及基於 cookie 機制實現 oa 十天免登錄的功能 @ 每博一文案 嘿,大風揚起的沉沙中,每一粒都有它的必然性,而每個人的命運都有自己的因果, 為自己的選擇負責承擔或好或壞的結果。是成年人的必修課。 有人請教索羅斯投資的指導,我的父親一直追隨你炒股 ...
  • JSP 的本質原理解析:"編寫的時候是JSP,心裡想解讀的是 java 源碼" @ 每博一文案 活明白的人,一生只做好了這兩件事: 每個瞬間都充滿了選擇和承擔,就算面前是一座獨木橋,也必須選擇是前進後退,亦或是留在原地此時此刻你所經歷的一切。 這是過往無數個選擇後的結果,哪些小的選擇匯聚在了一起,最 ...
  • 一前言 題目集四主要考察的是對LocalDate,ArrayList,HashSet等Java自帶類的使用 題目集五主要考察的是對正則表達式的使用,以及對其題目集三的時間題目的進行類結構的改變 題目集六隻有一道題,主要是對題目集四的第一題進行加大難度 總的來說這幾次的題目量比前面幾次都要少,但是題目 ...
  • 構造 Socket 在【客戶端/服務端】的通信模式中,客戶端需要主動構造與伺服器連接的 Socket,構造方法有以下幾種重載形式: Socket() Socket(InetAddress address, int port) throws UnknownHostException,IOExcepti ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...