1. 程式人生 > >老司機解說Go語言之資料結構

老司機解說Go語言之資料結構

當向一個新程式設計師解釋Go語言時,我發現如果解釋Go的資料是如何在記憶體中表示的,將有助於建立編寫高效程式的良好直覺。

基礎型別

讓我們從一些簡單的例子開始:

變數i是int型別,在記憶體中佔用一個32位的儲存單位。(上圖拿32位系統來舉例;對以上的例子,只有指標才會在64位的機器上佔用更多的空間——int始終是32位——然而我們仍然可以選擇64位的系統。)

變數j是int32型別,因為它經過了顯式的型別轉化。儘管i和j有著同樣的記憶體佈局,但它們的型別是不一樣的:像這樣的賦值i = j會產生型別異常,必須通過顯式的型別轉換:i = int(j)   。

變數f是個浮點型別,上例中它代表著佔用32位的浮點值。它的記憶體佔用跟int32一樣,但內部佈局不同。

結構與指標

 變數bytes是[5]byte型別,一個具有5個位元組的陣列。它的記憶體表示就只有5個緊挨著的位元組,就像C裡的陣列一樣。相似地,primes變數是一個擁有4個int數值的陣列。

Go就像C而不像Java,它讓程式設計師決定什麼是或者不是一個指標。拿這個型別定義來舉例:

?
1 type Point struct { X , Y int }

定義一個叫Point的簡單的結構型別,意味著記憶體裡是兩個相鄰的int。

Point{ 10, 20 }這句複合語法表示一個被初始化的Point物件。而&Point{ 10, 20 }這句則表示一個指向被初始化的Point物件的指標。前者在記憶體中有兩個資料塊,而後者則存放著一個指向兩個資料塊的指標。

結構中的欄位被依次地排列在記憶體裡面。

?
1 2 type Rect1 struct { Min, Max Point } type Rect2 struct { Min, Max *Point }

Rect1是一個擁有兩個Point型別欄位的結構,它的一條記錄包含了兩條Point記錄——共4個int。Rect2是一個擁有兩個Point型別指標的結構,在記憶體裡它佔兩個Point指標的空間。

用過C的程式設計師也許對Point欄位和*Point欄位的區別並不陌生,而只用過Java或者Python(或者其他)則可能為需要做出選擇而驚訝。通過為程式設計師提供控制基礎記憶體佈局的可能,Go語言讓程式設計師可以操控所有資料結構總尺寸、所分配變數的總數和記憶體訪問的模式,這些對於建造高效能系統都至關重要。

字串

接下來我們繼續看一些更有趣的資料型別。

(灰色箭頭意味著實現上的真實表示方式,但這在程式設計過程中是不可見的)

一個字串在記憶體中的表示被分成兩段,一個指向字串資料的指標和一個長度值。因為字串是可列舉的,所以多個字串共享同一段儲存空間也是安全的,因此如果對s字串進行一個切片選擇,將得到一個可能不一樣的指標和長度,但它們也指向同一段位元組序列。這意味著,切片並不需要分配空間或者是複製資料,建立切片很容易,只需要傳遞明確的下標值就行了。

(順帶一句,在Java和某些與嚴重有一個著名的缺陷,當你對一個字串進行切片並儲存其中的一小部分,引用將在記憶體中儲存原字串的完整內容,即使只有很小的一部分是被用到的。Go也有這個缺陷。要不然(我們嘗試但最終捨棄了),我們將對切片採取昂貴的做法——分配記憶體並拷貝資料——大部分語言都避免這種做法。

切片

切片是對陣列中一段資料的引用。在記憶體中它有三段資料組成:一個指向資料頭的指標、切片的長度、切片的容量。長度是索引操作的上界,如:x[i] 。容量是切片操作的上界,如:x[i:j]  。

跟對字串做切片一樣,對陣列進行切片也不會導致複製:它只建立一個存放指標、長度和容量的結構體。在這個例子中,語句[ ] int { 2, 3, 5, 7, 11 } 建立了一個包含5個值的新陣列,併為x切片設定了對應的值來描述那個陣列。切片表示式x[1:3]並不為資料分配記憶體:它只填充切片結構的欄位,用以複用陣列的儲存空間。在這個例子中,長度是2,y[0]和y[1]是僅有的合法資料;但容量是4,y[0:4]是個合法的切片表示式。(檢視高效GO獲取更多關於長度、容量,以及如何使用切片的資訊。)

由於切片不是指標而是多欄位的結構,切片操作並不需要分配記憶體,即使對於切片頭也是這樣,它可以常駐在棧中。這種表示法讓切片的使用的代價很低,就像C中傳遞精確的指標和長度一樣。Go原生地在切片中使用了指標,這也意味著每個切片操作都分配一個記憶體物件。即使有了一個更快的記憶體分配器,這為垃圾回收帶來了不必要的工作,並且我們發現,就像字串那個例子一樣,給於精確的下標,比進行切片操作好。大多數情況下,避免不必要的間接引用和記憶體分配可以讓切片足夠高效了。

new和make

Go有兩種建立資料結構的方法:new和make 。它們的區別是常見的早期困惑,但很快就會變得自然。基礎的區別在於,new(T)返回一個*T型別,一個可以被隱性反向引用的指標(如圖中的黑色指標),而make(T,args)返回一個原始的T,它並不是一個指標。T中常有寫隱性的指標(如圖中的灰色指標)。new返回一個指向初始化為全0值的指標,而make返回一個複雜的結構。

有一種方式讓兩者統一起來,它對於傳統的C和C++是一個重大的改變:定義make(*T)來返回一個指向新分配的T的指標,因此new(Point)和make(*Point)的效果是一致的。我們用這種方法嘗試了一段日子,但最終覺得這對於一些期待一個分配函式的人來說,實在太難以接受了。

相關推薦

司機解說Go語言資料結構

當向一個新程式設計師解釋Go語言時,我發現如果解釋Go的資料是如何在記憶體中表示的,將有助於建立編寫高效程式的良好直覺。 基礎型別 讓我們從一些簡單的例子開始: 變數i是int型別,在記憶體中佔用一個32位的儲存單位。(上圖拿32位系統來舉例;對以上的例子,只有指標才會在64位的機器上佔用

go語言-常見資料結構與演算法

選擇排序 //基礎版 func SelectionSort(arr []int, n int) { for i := 0; i < n; i ++ { minindex := i for j := i + 1; j < n; j++ { if arr[minindex

go語言基礎資料結構學習---- 陣列, 列表(list)和切片(slice)

go語言基礎資料結構學習–> 陣列, 列表(list)和切片(slice) go 語言中的 陣列是型別相同的元素的集合, 列表是雙鏈表的容器, 可以新增不同型別的資料 切片是對現有陣列的引用, 比陣列更方便靈活, 還可以追加資

go語言基礎資料結構學習 ---- 字典(map)

go語言基礎資料結構學習–> 字典(map) go 語言中的字典和python 中的字典特性差不多 相同: 鍵值對, 無序集合, 每個鍵都是唯一的, 對一個鍵多次賦值會更新當前鍵的值; 不同: go語言的字典裡面的型別是定好的, 不可變更, python可以隨

C語言資料結構

C語言中的基本結構體以及記憶體之間的關係,我們經常用到,所以我們今天來學習一下這些內容 記憶體 記憶體是什麼,和資料結構有什麼關係? 記憶體從哪裡來? 記憶體是程式執行的活動之地,程式需要放在記憶體中執行的,程式執行時需要記憶體來儲存一些臨時變數

Go語言項目的包結構

go語言 包結構Go語言的工作空間:通常添加到GOPATH中。 src bin pkg對於bin和pkg兩個目錄,主要影響go install/get命令,它們會將編譯結果安裝到這兩個目錄下,以實現增量編譯。環境變量用於實現GOPATH只是工具鏈和標準庫的存放位置。在使用Git等版本控制工具的時候,建議忽略

Go語言方法詳

go語言 方法方法是與對象實例綁定的特殊函數。用於維護和展示對象自身的狀態。對象是內斂的。普通函數則專註與算法流程,通過接受參數來完成特定的邏輯運算,並返回最終結果,方法是有關聯狀態的,函數通常是沒有的。方法和函數定義語法區別在於前者實例接受參數,編譯器以此確定方法所屬的類型。在一些語言中盡管沒有定義,但是函

Go語言讀取yaml配置文件,轉換成struct結構,json形式輸出

go yaml 1、例子1 1.1、yaml文件內容如下: host: localhost:3306 user: root pwd: 123456 dbname: test 1.2、代碼如下: //將yaml文件,轉換成對象,再轉換成json格式輸出 package main import (

go語言行--golang核武器goroutine調度原理、channel詳

-s 丟失 一半 內核調度 保留 dea 等等 ado 線程 一、goroutine簡介 goroutine是go語言中最為NB的設計,也是其魅力所在,goroutine的本質是協程,是實現並行計算的核心。goroutine使用方式非常的簡單,只需使用go關鍵字即可啟動一

C語言複習資料結構簡單的二叉樹輸入和輸出操作

C語言複習之簡單的二叉樹的僅輸入輸出操作 1:結構體 typedef struct TreeNode{ _Data value; struct TreeNode * father; struct TreeNode * right; stru

go語言監控資料

//在執行檔案中 // conf // |--app.conf // monitor.exe // app.conf中的內容 // command="ls -l" // monitordir ="c:\\test" // 在windows下會執行兩次 // https://github.co

微信標籤語言頁面結構檔案--資料繫結、條件渲染

頁面結構檔案      WXML(weixin markup language)是框架設計的一套標記語言,用於渲染介面,WXML的渲染原理,通過一套標記語言,在不同平臺被解析為不同端的渲染檔案。 WXML具有資料繫結、列表渲染、條件渲染、模板、事件等能力。

C語言基本資料結構二(二叉樹的三種遍歷,節點數以及深度演算法)

關於二叉樹的定義,網上有比較好的介紹,在這裡就簡單介紹二叉樹的一些性質 二叉樹的基本性質 1)二叉樹的第i層上至多有 2^(i-1)(i ≥1)個結點; 2)深度為 h 的二叉樹中至多含有 2^h – 1 個結點; 3)若在任意一棵二叉樹中,有 n0 個葉子結點,有 n2

go語言結構體陣列轉為string字串

轉換順序:先將struct結構體轉為map,再將map轉為string字串 struct --> map --> string type demo struct { Id

c語言資料結構線性表歸併大概

線性表:n個具有相同特性的資料元素的有限序列。較為靈活,可根據需要増長或縮短。 兩線性表歸併演算法:(此時已知其元素按值非遞減排列) void MergeList(List La, List Lb,

Go語言基本資料型別以及一些規範

go基本資料型別 1.bool,一個位元組,值是true或者false,不可以用0或者1表示(java中boolean佔用4個位元組,而boolean作為陣列出現時,每個boolean佔用1個位元組)

Go語言嵌入類型

go 類型 嵌入類型,或者嵌套類型,這是一種可以把已有的類型聲明在新的類型裏的一種方式,這種功能對代碼復用非常重要。在其他語言中,有繼承可以做同樣的事情,但是在Go語言中,沒有繼承的概念。Go提倡的代碼復用的方式是組合,所以這也是嵌入類型的意義所在。組合而不是繼承,所以Go才會更靈活。type Rea

Go語言標誌符可見性

goGo的標誌符,這個翻譯覺得怪怪的,不過還是按這個起了標題,可以理解為Go的變量、類型、字段等。這裏的可見性,也就是說那些方法、函數、類型或者變量字段的可見性。比如哪些方法不想讓另外一個包訪問,我們就可以把它們聲明為非公開的;如果需要被另外一個包訪問,就可以聲明為公開的,和Java語言裏的作用域類似。在Go

Go語言Doc 文檔

go doc 對於協作開發或者代碼共享來說,文檔是一個可以幫助開發者快速了解以及使用這些代碼的一個教程,文檔越全面、越詳細,入門越快,效率也會更高。在Go語言中,Go為我們提供了快速生成文檔以及查看文檔的工具,讓我們可以很容易地編寫查看文檔。Go提供了兩種查看文檔的方式:一種是使用go doc命令在終

Go語言並發資源競爭

go 並發 並發本身並不復雜,但是因為有了資源競爭的問題,就使得我們開發出好的並發程序變得復雜起來,因為會引起很多莫名其妙的問題。package main import ( "fmt" "runtime" "sync" ) var ( count int32