Go 学习笔记(4)— Go 标识符、数据类型之间转换、布尔型、整型、浮点型、interface 类型
1. 標識符整體分類
Go 標識符整體分類如下圖所示:
2. 數據類型分類
Go 語言按類別有以下幾種數據類型:
| 類型 | 描述 |
|---|---|
| 布爾型 | 布爾型的值只可以是常量 true 或者 false |
| 數字類型 | 整型 int 和浮點型 float32、float64,Go語言支持整型和浮點型數字,并且支持復數,其中位的運算采用補碼 |
| 字符串類型 | 字符串就是一串固定長度的字符連接起來的字符序列。 Go 語言的字符串的字節使用 UTF-8 編碼標識 Unicode 文本 |
| 派生類型 | (a) 指針類型(Pointer) (b) 數組類型 ? 結構化類型(struct) (d) Channel 類型 (e) 函數類型 (f) 切片類型 (g) 接口類型(interface) (h) Map 類型 |
注意:
- 布爾類型數據和整型數據不能直接進行轉換;
var a bool = true
a = 10 // cannot use 10 (type int) as type bool in assignment
a := 100
if a {// Error: non-bool a (type int) used as if conditionprintln("true")
}
Go語言不存在隱式類型轉換,因此所有的類型轉換都必須顯式的聲明,不同類型的不能直接轉化,必須強制類型轉換;
轉換方式:
valueOfTypeB = typeB(valueOfTypeA)
a := 5.0
b := int(a)
package mainfunc main() {var a int = 10var b int32 = 10b = a // cannot use a (type int) as type int32 in assignmentif a == b { // invalid operation: a == b (mismatched types int and int32)println("a is equal b ")}
}
- 對于整數類型值、整數常量之間的類型轉換,原則上只要源值在目標類型的可表示范圍內就是合法的。
Go 語言使用類型前置加括號的方式進行類型轉換 , 一般格式如下 :
T(表達式)
其中, T 代表要轉換的類型。表達式包括變量、復雜算子和函數返回值等。類型轉換 時 ,需要考慮兩種類型的關系和范圍,是否會發生數值截斷等。
比如,之所以 uint8(255) 可以把無類型的常量 255 轉換為 uint8 類型的值,是因為 255 在 [0, 255] 的范圍內。但需要特別注意的是,源整數類型的可表示范圍較大,而目標類型的可表示范圍較小的情況,比如把值的類型從 int16 轉換為 int8 。請看下面這段代碼:
var srcInt = int16(-255)dstInt := int8(srcInt)fmt.Println(dstInt) // 1
一定要記住,當整數值的類型的有效范圍由寬變窄時,只需在補碼形式下截掉一定數量的高位二進制數即可。
類似的快刀斬亂麻規則還有:當把一個浮點數類型的值轉換為整數類型值時,前者的小數部分會被全部截掉。
- 雖然直接把一個整數值轉換為一個
string類型的值是可行的,但值得關注的是,被轉換的整數值應該可以代表一個有效的Unicode代碼點,否則轉換的結果將會是"�"(僅由高亮的問號組成的字符串值)。
字符’�’的 Unicode 代碼點是U+FFFD。它是 Unicode 標準中定義的 Replacement Character,專用于替換那些未知的、不被認可的以及無法展示的字符。
string(-1) // "�"
總結:
package mainimport ("fmt"
)func main() {// 重點1的示例。var srcInt = int16(-255)// 請注意,之所以要執行uint16(srcInt),是因為只有這樣才能得到全二進制的表示。// 例如,fmt.Printf("%b", srcInt)將打印出"-11111111",后者是負數符號再加上srcInt的絕對值的補碼。// 而fmt.Printf("%b", uint16(srcInt))才會打印出srcInt原值的補碼"1111111100000001"。fmt.Printf("The complement of srcInt: %b (%b)\n", uint16(srcInt), srcInt)// The complement of srcInt: 1111111100000001 (-11111111)dstInt := int8(srcInt)fmt.Printf("The complement of dstInt: %b (%b)\n", uint8(dstInt), dstInt)// The complement of dstInt: 1 (1)fmt.Printf("The value of dstInt: %d\n", dstInt)// The value of dstInt: 1fmt.Println()// 重點2的示例。fmt.Printf("The Replacement Character: %s\n", string(-1))// The Replacement Character: �fmt.Printf("The Unicode codepoint of Replacement Character: %U\n", '�')// The Unicode codepoint of Replacement Character: U+FFFDfmt.Println()
}
2.1 數字類型詳細劃分
| 類型 | 描述 |
|---|---|
| uint8 | 無符號 8 位整型 (0 到 255) |
| uint16 | 無符號 16 位整型 (0 到 65535) |
| uint32 | 無符號 32 位整型 (0 到 4294967295) |
| uint64 | 無符號 64 位整型 (0 到 18446744073709551615) |
| int8 | 有符號 8 位整型 (-128 到 127) |
| int16 | 有符號 16 位整型 (-32768 到 32767) |
| int32 | 有符號 32 位整型 (-2147483648 到 2147483647) |
| int64 | 有符號 64 位整型 (-9223372036854775808 到 9223372036854775807) |
盡管在某些特定的運行環境下 int 、 uint 和 uintptr 的大小可能相等,但是它們依然是不同的類型,比如 int 和 int32 ,雖然 int 類型的大小也可能是 32 bit,但是在需要把 int 類型當做 int32 類型使用的時候必須顯示的對類型進行轉換,反之亦然。
在 Golang 中,整數類型長度與操作系統有關,32 位系統中,整數類型長度是 4 個字節,在 64 位系統中,整數類型長度是 8 個字節。
Go 語言中有符號整數采用 2 的補碼形式表示,也就是最高 bit 位用來表示符號位,一個 n-bit 的有符號數的取值范圍是從 -2 到 2-1。
無符號整數的所有 bit 位都用于表示非負數,取值范圍是 0 到 2。例如,int8 類型整數的取值范圍是從 -128 到 127,而 uint8 類型整數的取值范圍是從 0 到 255。
哪些情況下使用 int 和 uint ?
程序邏輯對整型范圍沒有特殊需求。例如,對象的長度使用內建 len() 函數返回,這個長度可以根據不同平臺的字節長度進行變化。實際使用中,切片或 map 的元素數量等都可以用 int 來表示。
事實上,內置的 len 函數返回一個有符號的 int ,我們可以像下面例子那樣處理逆序循環。
medals := []string{"gold", "silver", "bronze"}
for i := len(medals) - 1; i >= 0; i-- {fmt.Println(medals[i]) // "bronze", "silver", "gold"
}
如果 len 函數返回一個無符號數,那么 i 也將是無符號的 uint 類型,然后條件i >= 0則永遠為真。在三次迭代之后,也就是i == 0時, i-- 語句將不會產生 -1,而是變成一個 uint 類型的最大值(可能是2^64-1),然后 medals[i] 表達式運行時將發生 panic 異常,也就是試圖訪問一個 slice 范圍以外的元素。
反之,在二進制傳輸、讀寫文件的結構描述、位運算、哈希和加密操作等時,為了保持文件的結構不會受到不同編譯目標平臺字節長度的影響,通常使用 uint 。
Unicode 字符 rune 類型是和 int32 等價的類型,通常用于表示一個 Unicode 碼點。這兩個名稱可以互換使用。同樣 byte 也是 uint8 類型的等價類型, byte 類型一般用于強調數值是一個原始的數據而不是一個小的整數。
一個算術運算的結果,不管是有符號或者是無符號的,如果需要更多的 bit 位才能正確表示的話,就說明計算結果是溢出了。超出的高位的 bit 位部分將被丟棄。如果原始的數值是有符號類型,而且最左邊的 bit 位是1的話,那么最終結果可能是負的,例如 int8 的例子:
var u uint8 = 255
fmt.Println(u, u+1, u*u) // "255 0 1"
var i int8 = 127
fmt.Println(i, i+1, i*i) // "127 -128 1"
2.2 浮點類型詳細劃分
| 類型 | 描述 |
|---|---|
| float32 | IEEE-754 32位浮點型數 |
| float64 | IEEE-754 64位浮點型數 |
| complex64 | 32 位實數和虛數 |
| complex128 | 64 位實數和虛數 |
-
float32的浮點數的最大范圍約為 3.4e38, 可以使用常量定義:math.MaxFloat32。 -
float64的浮點數的最大范圍約為 1.8e308,可以使用常量定義:math.MaxFloat64。 -
float32類型的浮點數可以提供大約 6 個十進制數的精度, -
float64則可以提供約 15 個十進制數的精度;
通常應該優先使用 float64 類型,因為 float32 類型的累計計算誤差很容易擴散,并且 float32 能精確表示的正整數并不是很大(譯注:因為 float32 的有效 bit 位只有23個,其它的 bit 位用于指數和符號;當整數大于 23 bit 能表達的范圍時, float32 的表示將出現誤差):
var f float32 = 16777216 // 1 << 24
fmt.Println(f == f+1) // "true"!
用 Printf 函數的 %g 參數打印浮點數,將采用更緊湊的表示形式打印,并提供足夠的精度,但是對應表格的數據,使用 %e (帶指數)或 %f 來控制保留幾位小數的形式打印可能更合適。所有的這三個打印形式都可以指定打印的寬度和控制打印精度。
for x := 0; x < 8; x++ {fmt.Printf("x = %d e^x = %8.3f\n", x, math.Exp(float64(x)))
}
如果只保留小數部分的精度,則使用 %.3f 表示保留 3 位小數的精度。
上面代碼打印 e 的冪,打印精度是小數點后三個小數精度和 8 個字符寬度:
x = 0 e^x = 1.000
x = 1 e^x = 2.718
x = 2 e^x = 7.389
x = 3 e^x = 20.086
x = 4 e^x = 54.598
x = 5 e^x = 148.413
x = 6 e^x = 403.429
x = 7 e^x = 1096.633
Go語言提供了兩種精度的復數類型: complex64 和 complex128 ,分別對應 float32 和 float64 兩種浮點數精度。內置的 complex 函數用于構建復數,內建的 real 和 imag 函數分別返回復數的實部和虛部:
var x complex128 = complex(1, 2) // 1+2i
var y complex128 = complex(3, 4) // 3+4i
fmt.Println(x*y) // "(-5+10i)"
fmt.Println(real(x*y)) // "-5"
fmt.Println(imag(x*y)) // "10"
如果一個浮點數面值或一個十進制整數面值后面跟著一個 i ,例如 3.141592i或 2i,它將構成一個復數的虛部,復數的實部是 0:
fmt.Println(1i * 1i) // "(-1+0i)", i^2 = -1
在常量算術規則下,一個復數常量可以加到另一個普通數值常量(整數或浮點數、實部或虛部),我們可以用自然的方式書寫復數,就像 1+2i 或與之等價的寫法 2i+1 。上面 x 和 y 的聲明語句還可以簡化:
x := 1 + 2i
y := 3 + 4i
復數也可以用 == 和 != 進行相等比較。只有兩個復數的實部和虛部都相等的時候它們才是相等的(譯注:浮點數的相等比較是危險的,需要特別小心處理精度問題)。
參考:Go 語言圣經
2.3 其它數字類型劃分
| 類型 | 描述 |
|---|---|
| byte | 等同 uint8 |
| rune | 等同 int32 |
| uint | 32 或 64 位 |
| int | 與 uint 一樣大小 |
| uintptr | 無符號整型,用于存放一個指針 |
Go 語言內建的基本類型中就存在兩個別名類型。byte 是 uint8 的別名類型,而 rune 是 int32 的別名類型。
3. interface 類型
由于 Go 語言中任何對象實例都滿足空接口 interface{} ,所以 interface{} 看起來像是可以指向任何對象的 Any 類型,如下:
var v1 interface{} = 1 // 將int類型賦值給interface{}
var v2 interface{} = "abc" // 將string類型賦值給interface{}
var v3 interface{} = &v2 // 將*interface{}類型賦值給interface{}
var v4 interface{} = struct{ X int }{1}
var v5 interface{} = &struct{ X int }{1}
當函數可以接受任意的對象實例時,我們會將其聲明為 interface{} ,最典型的例子是標準庫 fmt 中 PrintXXX 系列的函數,例如:
func Printf(fmt string, args ...interface{})
func Println(args ...interface{})
在 Go 語言中,interface{} 代表空接口,任何類型都是它的實現類型。任何類型的值都可以很方便地被轉換成空接口的值就行了,這里的具體語法是 interface{}(x) 。
你可能會對這里的 {} 產生疑惑,為什么在關鍵字 interface 的右邊還要加上這個東西?請記住,一對不包裹任何東西的花括號,除了可以代表空的代碼塊之外,還可以用于表示不包含任何內容的數據結構(或者說數據類型)。
而空接口 interface{} 則代表了不包含任何方法定義的、空的接口類型。當然了,對于一些集合類的數據類型來說,{} 還可以用來表示其值不包含任何元素,比如空的切片值 []string{} ,以及空的字典值 map[int]string{} 。
總結
以上是生活随笔為你收集整理的Go 学习笔记(4)— Go 标识符、数据类型之间转换、布尔型、整型、浮点型、interface 类型的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 乌镇不买门票可以玩哪些地方
- 下一篇: Go 学习笔记(6)— 变量定义、变量声