本文目錄一覽:
- 1、這可能是最全的golang的”==”比較規則了吧
- 2、GoLang中的切片擴容機制
- 3、golang如何創建目錄
- 4、golang中的傳值或傳引用
- 5、基礎知識 – Golang 中的格式化輸入輸出
這可能是最全的golang的”==”比較規則了吧
大家經常用”==”來比較兩個變數是否相等。但是golang中的”==”有很多細節的地方,跟php是不一樣的。很多時候不能直接用”==”來比較,編譯器會直接報錯。
golang中基本類型的比較規則和複合類型的不一致,先介紹下golang的變數類型:
golang中的基本類型
比較的兩個變數類型必須相等。而且,golang沒有隱式類型轉換,比較的兩個變數必須類型完全一樣,類型別名也不行。如果要比較,先做類型轉換再比較。
複合類型是逐個欄位,逐個元素比較的。需要注意的是, array 或者struct中每個元素必須要是可比較的,如果某個array的元素 or struct的成員不能比較(比如是後面介紹的slice,map等),則此複合類型也不能比較。
逐個成員比較類型和值。每個對應成員的比較遵循基本類型變數的比較規則。
但是如果struct中有不可比較的成員類型時:
可以看到,struct中有slice這種不可比較的成員時,整個struct都不能做比較,即使沒有對slice那個成員賦值(slice默認值為nil)
slice和map的比較規則比較奇怪,我們先說普通的變數引用類型val和channel的比較規則。
引用類型變數存儲的是某個變數的內存地址。所以引用類型變數的比較,判斷的是這兩個引用類型存儲的是不是同一個變數。
上面看起來比較廢話,但是得理解引用類型的含義。不然對判斷規則還是不清楚。
slice類型不可比較,只能與零值nil做比較。
關於slice類型不可比較的原因,後面會專門寫文章做討論。
map類型和slice一樣,不能比較,只能與nil做比較。
介面類型的變數,包含該介面變數存儲的值和值的類型兩部分組成,分別稱為介面的動態類型和動態值。 只有動態類型和動態值都相同時,兩個介面變數才相同:
而且介面的動態類型必須要是可比較的,如果不能比較(比如slice,map),則運行時會報panic。因為編譯器在編譯時無法獲取介面的動態類型,所以編譯能通過,但是運行時直接panic:
golang的func作為一等公民,也是一種類型,而且不可比較
上面說過,map和slice是不可比較類型,但是有沒有特殊的方法來對slice和map做比較呢,有
reflect.DeepEqual函數可以用來比較兩個任意類型的變數
對map類型做比較:
對slice類型做比較:
對struct類型做比較:
可以發現,只要變數的類型和值相同的話,reflect.DeepEqual比較的結果就為true
直接看用例:
結果為:
1, golang的類型再定義和類型別名
2,golang的slice和map為什麼不可以比較
1,
2,
3,
GoLang中的切片擴容機制
[5]int 是數組,而 []int 是切片。二者看起來相似,實則是根本上不同的數據結構。
切片的數據結構中,包含一個指向數組的指針 array ,當前長度 len ,以及最大容量 cap 。在使用 make([]int, len) 創建切片時,實際上還有第三個可選參數 cap ,也即 make([]int, len, cap) 。在不聲明 cap 的情況下,默認 cap=len 。當切片長度沒有超過容量時,對切片新增數據,不會改變 array 指針的值。
當對切片進行 append 操作,導致長度超出容量時,就會創建新的數組,這會導致和原有切片的分離。在下例中
由於 a 的長度超出了容量,所以切片 a 指向了一個增長後的新數組,而 b 仍然指向原來的老數組。所以之後對 a 進行的操作,對 b 不會產生影響。
試比較
本例中, a 的容量為6,因此在 append 後並未超出容量,所以 array 指針沒有改變。因此,對 a 進行的操作,對 b 同樣產生了影響。
下面看看用 a := []int{} 這種方式來創建切片會是什麼情況。
可以看到,空切片的容量為0,但後面向切片中添加元素時,並不是每次切片的容量都發生了變化。這是因為,如果增大容量,也即需要創建新數組,這時還需要將原數組中的所有元素複製到新數組中,開銷很大,所以GoLang設計了一套擴容機制,以減少需要創建新數組的次數。但這導致無法很直接地判斷 append 時是否創建了新數組。
如果一次添加多個元素,容量又會怎樣變化呢?試比較下面兩個例子:
那麼,是不是說,當向一個空切片中插入 2n-1 個元素時,容量就會被設置為 2n 呢?我們來試試其他的數據類型。
可以看到,根據切片對應數據類型的不同,容量增長的方式也有很大的區別。相關的源碼包括: src/runtime/msize.go , src/runtime/mksizeclasses.go 等。
我們再看看切片初始非空的情形。
可以看到,與剛剛向空切片添加5個int的情況一致,向有3個int的切片中添加2個int,容量增長為6。
需要注意的是, append 對切片擴容時,如果容量超過了一定範圍,處理策略又會有所不同。可以看看下面這個例子。
具體為什麼會是這樣的變化過程,還需要從 源碼 中尋找答案。下面是 src/runtime/slice.go 中的 growslice 函數中的核心部分。
GoLang中的切片擴容機制,與切片的數據類型、原本切片的容量、所需要的容量都有關係,比較複雜。對於常見數據類型,在元素數量較少時,大致可以認為擴容是按照翻倍進行的。但具體情況需要具體分析。
golang如何創建目錄
golang中關於目錄與文件名等操作都在os這個包中,具體的創建目錄都是通過Mkdir和MkdirAll這2個函數來實現的,這兩個函數用法一致
os.Mkdir(dirName string, perm FileMode)
dirName即要創建的目錄(文件夾路徑),可以是絕對路徑,也可以是相對路徑(相對於GOPATH)
perm表示創建的目錄的許可權,如0777(讀r許可權值為4,寫許可權w值為2,執行許可權x值為1)
如:我要在/data/program/goapp這個目錄下創建一個golang這個子目錄,示例如下:
package main
import (
“os”
“fmt”
)
func main() {
err := os.Mkdir(“/data/program/goapp/golang”, 0666)
if err != nil {
fmt.Println(err)
}
}
註:Mkdir和MkdirAll的區別
Mkdir創建目錄,它的父級目錄必須是存在的,不然創建會失敗
MkdirAll可以遞歸創建目錄,即只要根目錄存在即可,如下:
err := os.MkdirAll(“/data/program/goapp/golang/test/hello”, 0766)
if err != nil {
fmt.Println(err)
}
本例中:/data/program/goapp是已經存在的目錄,而子目錄golang/test/hello是不存在,此時要使用MkdirAll來創建
golang中的傳值或傳引用
按數據類別有以下幾種數據類型:
按存儲方式也有兩大類數據類型:
值類型:變數直接存儲值。值類型的數據存儲在棧內存空間中,棧在函數調f返回後,內存會被釋放。
引用類型:變數存儲的是一個地址,這個地址存儲最終的值。引用數據類型的數據存儲在堆內存空間中,通過 GC 回收。
函數調用時申明的基礎類型均為值傳遞,如int,string,數組等,數據傳入函數後會重新copy一份,函數內的修改不會影響外面的變數,外部變數的修改也不會影響函數類的變數。
func main () {
myvar := [ 4 ] string {” test0 “, ” test1 “, ” test3 “, ” test4 “}
go Test (myvar)
for i := 1 ; i
基礎知識 – Golang 中的格式化輸入輸出
【格式化輸出】
// 格式化輸出:將 arg 列表中的 arg 轉換為字元串輸出
// 使用動詞 v 格式化 arg 列表,非字元串元素之間添加空格
Print(arg列表)
// 使用動詞 v 格式化 arg 列表,所有元素之間添加空格,結尾添加換行符
Println(arg列表)
// 使用格式字元串格式化 arg 列表
Printf(格式字元串, arg列表)
// Print 類函數會返回已處理的 arg 數量和遇到的錯誤信息。
【格式字元串】
格式字元串由普通字元和佔位符組成,例如:
“abc%+ #8.3[3]vdef”
其中 abc 和 def 是普通字元,其它部分是佔位符,佔位符以 % 開頭(註:%% 將被轉義為一個普通的 % 符號,這個不算開頭),以動詞結尾,格式如下:
%[旗標][寬度][.精度][arg索引]動詞
方括弧中的內容可以省略。
【旗標】
旗標有以下幾種:
空格:對於數值類型的正數,保留一個空白的符號位(其它用法在動詞部分說明)。
0 :用 0 進行寬度填充而不用空格,對於數值類型,符號將被移到所有 0 的前面。
其中 “0” 和 “-” 不能同時使用,優先使用 “-” 而忽略 “0”。
【寬度和精度】
「寬度」和「精度」都可以寫成以下三種形式:
數值 | * | arg索引*
其中「數值」表示使用指定的數值作為寬度值或精度值,「 」表示使用當前正在處理的 arg 的值作為寬度值或精度值,如果這樣的話,要格式化的 arg 將自動跳轉到下一個。「arg索引 」表示使用指定 arg 的值作為寬度值或精度值,如果這樣的話,要格式化的 arg 將自動跳轉到指定 arg 的下一個。
寬度值:用於設置最小寬度。
精度值:對於浮點型,用於控制小數位數,對於字元串或位元組數組,用於控制字元數量(不是位元組數量)。
對於浮點型而言,動詞 g/G 的精度值比較特殊,在適當的情況下,g/G 會設置總有效數字,而不是小數位數。
【arg 索引】
「arg索引」由中括弧和 arg 序號組成(就像上面示例中的 [3]),用於指定當前要處理的 arg 的序號,序號從 1 開始:
‘[‘ + arg序號 + ‘]’
【動詞】
「動詞」不能省略,不同的數據類型支持的動詞不一樣。
[通用動詞]
v:默認格式,不同類型的默認格式如下:
布爾型:t
整 型:d
浮點型:g
複數型:g
字元串:s
通 道:p
指 針:p
無符號整型:x
T:輸出 arg 的類型而不是值(使用 Go 語法格式)。
[布爾型]
t:輸出 true 或 false 字元串。
[整型]
b/o/d:輸出 2/8/10 進位格式
x/X :輸出 16 進位格式(小寫/大寫)
c :輸出數值所表示的 Unicode 字元
q :輸出數值所表示的 Unicode 字元(帶單引號)。對於無法顯示的字元,將輸出其轉義字元。
U :輸出 Unicode 碼點(例如 U+1234,等同於字元串 “U+%04X” 的顯示結果)
對於 o/x/X:
如果使用 “#” 旗標,則會添加前導 0 或 0x。
對於 U:
如果使用 “#” 旗標,則會在 Unicode 碼點後面添加相應的 ‘字元’(前提是該字元必須可顯示)
[浮點型和複數型]
b :科學計數法(以 2 為底)
e/E:科學計數法(以 10 為底,小寫 e/大寫 E)
f/F:普通小數格式(兩者無區別)
g/G:大指數(指數 = 6)使用 %e/%E,其它情況使用 %f/%F
[字元串或位元組切片]
s :普通字元串
q :雙引號引起來的 Go 語法字元串
x/X:十六進位編碼(小寫/大寫,以位元組為元素進行編碼,而不是字元)
對於 q:
如果使用了 “+” 旗標,則將所有非 ASCII 字元都進行轉義處理。
如果使用了 “#” 旗標,則輸出反引號引起來的字元串(前提是
字元串中不包含任何製表符以外的控制字元,否則忽略 # 旗標)
對於 x/X:
如果使用了 ” ” 旗標,則在每個元素之間添加空格。
如果使用了 “#” 旗標,則在十六進位格式之前添加 0x 前綴。
[指針類型]
p :帶 0x 前綴的十六進位地址值。
[符合類型]
複合類型將使用不同的格式輸出,格式如下:
結 構 體:{欄位1 欄位2 …}
數組或切片:[元素0 元素1 …]
映 射:map[鍵1:值1 鍵2:值2 …]
指向符合元素的指針:{}, [], map[]
複合類型本身沒有動詞,動詞將應用到複合類型的元素上。
結構體可以使用 “+v” 同時輸出欄位名。
【注意】
1、如果 arg 是一個反射值,則該 arg 將被它所持有的具體值所取代。
2、如果 arg 實現了 Formatter 介面,將調用它的 Format 方法完成格式化。
3、如果 v 動詞使用了 # 旗標(%#v),並且 arg 實現了 GoStringer 介面,將調用它的 GoString 方法完成格式化。
如果格式化操作指定了字元串相關的動詞(比如 %s、%q、%v、%x、%X),接下來的兩條規則將適用:
4。如果 arg 實現了 error 介面,將調用它的 Error 方法完成格式化。
5。如果 arg 實現了 string 介面,將調用它的 String 方法完成格式化。
在實現格式化相關介面的時候,要避免無限遞歸的情況,比如:
type X string
func (x X) String() string {
return Sprintf(“%s”, x)
}
在格式化之前,要先轉換數據類型,這樣就可以避免無限遞歸:
func (x X) String() string {
return Sprintf(“%s”, string(x))
}
無限遞歸也可能發生在自引用數據類型上面,比如一個切片的元素引用了切片自身。這種情況比較罕見,比如:
a := make([]interface{}, 1)
a[0] = a
fmt.Println(a)
【格式化輸入】
// 格式化輸入:從輸入端讀取字元串(以空白分隔的值的序列),
// 並解析為具體的值存入相應的 arg 中,arg 必須是變數地址。
// 字元串中的連續空白視為單個空白,換行符根據不同情況處理。
// \r\n 被當做 \n 處理。
// 以動詞 v 解析字元串,換行視為空白
Scan(arg列表)
// 以動詞 v 解析字元串,換行結束解析
Scanln(arg列表)
// 根據格式字元串中指定的格式解析字元串
// 格式字元串中的換行符必須和輸入端的換行符相匹配。
Scanf(格式字元串, arg列表)
// Scan 類函數會返回已處理的 arg 數量和遇到的錯誤信息。
【格式字元串】
格式字元串類似於 Printf 中的格式字元串,但下面的動詞和旗標例外:
p :無效
T :無效
e/E/f/F/g/G:功能相同,都是掃描浮點數或複數
s/v :對字元串而言,掃描一個被空白分隔的子串
對於整型 arg 而言,v 動詞可以掃描帶有前導 0 或 0x 的八進位或十六進位數值。
寬度被用來指定最大掃描寬度(不會跨越空格),精度不被支持。
如果 arg 實現了 Scanner 介面,將調用它的 Scan 方法掃描相應數據。只有基礎類型和實現了 Scanner 介面的類型可以使用 Scan 類方法進行掃描。
【注意】
連續調用 FScan 可能會丟失數據,因為 FScan 中使用了 UnreadRune 對讀取的數據進行撤銷,而參數 io.Reader 只有 Read 方法,不支持撤銷。比如:
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/296063.html