《Go学习笔记 . 雨痕》类型
一、基本類型
清晰完備的預定義基礎類型,使得開發跨平臺應用時無須過多考慮符合和長度差異。
| 類型 | 長度 | 默認值 | 說明 | 
|---|---|---|---|
| bool | 1 | false | |
| byte | 1 | 0 | uint8 | 
| int, uint | 4, 8 | 0 | 默認整數類型,依據目標平臺,32 或 64 位 | 
| int8, uint8 | 1 | 0 | -128 ~ 127,0 ~ 125 | 
| int16, uint16 | 2 | 0 | -32,768 ~ 32,767,0 ~ 65,535 | 
| int32, uint32 | 4 | 0 | -21億 ~ 21億,0 ~ 42億 | 
| int64, uint64 | 8 | 0 | |
| float32 | 4 | 0.0 | |
| float64 | 8 | 0.0 | 默認浮點數類型 | 
| complex64 | 8 | ||
| complex128 | 16 | ||
| rune | 4 | 0 | Unicode Code Point, int32 | 
| uintptr | 4, 8 | 0 | 足以存儲指針的 uint | 
| string | "" | 字符串,默認值為空字符串,而非 NULL | |
| array | 數組 | ||
| struct | 結構體 | ||
| function | nil | 函數 | |
| interface | nil | 接口 | |
| map | nil | 字典,引用類型 | |
| slice | nil | 切片,引用類型 | |
| channel | nil | 通道,引用類型 | 
支持八進制、十進制以及科學計數法。標準庫 math 定義了各數字類型的取值范圍。
import (
	"fmt"
	"math"
)
func main()  {
	a, b, c := 100, 0144, 0x64
	fmt.Println(a, b, c)
	fmt.Printf("0b%b, %#o, %#x
", a, a, a)
	fmt.Println(math.MinInt8, math.MaxInt8)
}
輸出:
100 100 100 0b1100100, 0144, 0x64 -128 127
標準庫 strconv 可在不同進制(字符串)間轉換。
import (
	"strconv"
)
func main()  {
	a, _ := strconv.ParseInt("1100100", 2, 32)
	b, _ := strconv.ParseInt("0144", 8, 32)
	c, _ := strconv.ParseInt("64", 16, 32)
	println(a, b, c)
	println("0b" + strconv.FormatInt(a, 2))
	println("0" + strconv.FormatInt(a, 8))
	println("0x" + strconv.FormatInt(a, 16))
}
輸出:
100 100 100 0b1100100 0144 0x64
使用浮點數時,須注意小數位的有效精度,相關細節可參考 IEEE-754 標準。
func main()  {
	var a float32 = 1.1234567899	// 注意:默認浮點數類型是 float64
	var b float32 = 1.12345678
	var c float32 = 1.123456781
	println(a, b, c)
	println(a == b, a == c)
	fmt.Printf("%v %v, %v
", a, b, c)
}
輸出:
+1.123457e+000 +1.123457e+000 +1.123457e+000 true true 1.1234568 1.1234568, 1.1234568
別名
在官方的語言規范中,專門提到 兩個 別名。
byte alias for unit8
rune alias for unit32
別名類型無須轉換,可直接賦值。
func test(x byte) {
	println(x)
}
func main()  {
	var a byte = 0x11
	var b uint8 = a
	var c uint8 = a + b
	test(c)
}
但這并不表示,擁有相同底層結構的就屬于別名。就算在 64位 平臺上 int 和 int64 結構完全一致,也分屬不同類型,須顯式轉換。
func add(x, y int) int {
	return x + y
}
func main() {
	var x int = 100
	var y int64 = x		// 錯誤:cannot use x (type int) as type int64 in assignment
	add(x, y)			// 錯誤:cannot use y (type int64) as type int in argument to add
}
二、引用類型
所謂引用類型(reference type),特指 slice 、map 、channel 這三種預定義類型。相比 數字 、數組 等類型,引用類型 擁有更復雜的存儲結構。除分配內存外,他們還須初始化一系列屬性,諸如 指針 、長度 ,甚至包括哈希分布、數據隊列等。
內置函數 new() 按指定類型長度分配零值內存,返回指針,并不關心類型內部結構和初始化方式。而 引用類型 則必須使用 make() 函數創建,編譯器會將 make() 轉換為目標類型專用的創建函數(或指令),以確保完成全部內存分配和相關屬性初始化。
// test.go 文件
package main
func mkslice() []int  {
	s := make([]int, 0, 10)
	s = append(s, 100)
	return s
}
func mkmap() map[string]int {
	m := make(map[string]int)
	m["a"] = 1
	return m
}
func main() {
	m := mkmap()
	println(m["a"])
	s := mkslice()
	println(s[0])
}
輸出:
$ go build -gcflags "-l"  // 禁用函數內聯
$ go tool objdump -s "main.mk" test
TEXT main.mkslie(SB) test.go
    CALL runtime.makeslice(SB)
TEXT main.mkmap(SB) test.go
    CALL runtime.makemap(SB)    
除 new() / make() 函數外,也可用 初始化表達式,編譯器生成的指令基本相同。
當然,new() 函數也可為引用類型分配內存,但這是不完整創建。以字典(map)為例,它僅分配了字典類型本身(實際就是個指針包裝)所需內存,并沒有分配鍵值存儲內存,也沒有初始化散列桶等內部屬性,因此它無法正常工作。
import "fmt"
func main() {
	p := new(map[string]int)  // 函數 new 返回指針
	m := *p
	m["a"] = 1  // 報錯:panic: assignment to entry in nil map [運行期錯誤]
	fmt.Println(m)
}
三、類型轉換
隱式轉換造成的問題遠大于它帶來的好處。
除 常量 、別名類型 以及 未命名類型 外,Go 強制要求使用顯示類型轉換。加上不支持操作符重載,所以我們總是能確定語句及表達式的明確含義。
func main() {
	a := 10
	b := byte(a)
	c := a + int(b) // 混合類型表達式必須確保類型一致
	fmt.Println(c)
}
同樣不能講 非bool 類型結果當作 true/false 使用。
func main() {
	x := 100
	var b bool = x // 報錯:cannot use x (type int) as type bool in assignment
	if x { // 報錯:non-bool x (type int) used as if condition
	}
}
語法歧義
如果轉換的目標 指針 、單向通道 或 沒有返回值的函數 類型,那么必須使用 括號(),以避免造成語法分解錯誤。
func main() {
	x := 100
	p := *int(&x)	// 報錯:cannot convert &x (type *int) to type int
					// invalid indirect of int(&x) (type int)
	println(p)
}
正確的做法是用括號,讓編譯器將 *int 解析為指針類型。
(*int)(p) --> 如果沒有括號 --> *(int(p))
(<-chan int)(c) <-(chan int(c))
(func())(x) func() xfunc() int (x) --> 有返回值的函數類型可省略括號,但依然建議使用。
(func() int) (x) 使用括號后,更易閱讀
四、自定義類型
使用關鍵字 type 定義用戶自定義類型,包括基于現有基礎類型創建,或者是 結構體 、函數類型 等。
type flags byte
const (
	read flags = 1 << iota
	write
	exec
)
func main() {
	f := read | exec
	fmt.Printf("%b
", f)  // 輸出二進制標志位
}
輸出:
101
和 var 、const 類似,多個 type 定義可以合并成組,可在 函數 或 代碼塊內定義局部類型。
func main() {
	type (					// 組
		user struct {		// 結構體
			name string
			age  uint8
		}
		event func(string) bool // 函數類型
	)
	u := user{"Tom", 20}
	fmt.Println(u)
	var f event = func(s string) bool {
		println(s)
		return s != ""
	}
	f("abc")
}
輸出:
{Tom 20}
abc
即便指定了基礎類型,也只表明它們有相同底層數據結構,兩者間不存在任何關系,屬完全不同的兩種類型。除操作符外,自定義類型不會繼承基礎類型的其他信息(包括方法)。不能視作別名,不能隱式轉換,不能直接用于比較表達式。
func main() {
	type data int
	var d data = 10
	var x int = d  // 錯誤:annot use d (type data) as type int in assignment
	println(x)
	println(d == x) // 錯誤:invalid operation: d == x (mismatched types data and int)
}
未命名類型
與有明確標識符的 bool 、int、string 等類型相比,數組 、切片 、字典 、通道 等類型與具體元素類型或長度等屬性有關,故稱作 未命名類型(unnamed type)。當然,可用 type 為其提供 具體名稱,將其改變為 命名類型(named type)。
具有相同聲明的未命名類型視作同一類型。
具有相同基類型的指針;
具有相同元素類型 和 長度的數組(array);
具有相同元素類型的切片(slice);
具有相同鍵值類型的字典(map);
具有相同數據類型及操作方向的通道(channel);
具有相同字段序列(字段名、字段類型、標簽,以及字段順序)的結構體(struct);
具有相同簽名(參數和返回值列表,不包括參數名)的函數(func);
具有相同方法集(方法名、方法簽名,不包括順序)的接口(interface);
容易被忽視的是 struct tag,它也屬于類型組成部分,而不僅僅是元數據描述。
func main() {
	var a struct { // 匿名結構類型
		x int `X`
		s string `S`
	}
	var b struct {
		x int
		s string
	}
	b = a // 錯誤:cannot use a (type struct { x int "X"; s string "S" }) as type struct { x int; s string } in assignment
	fmt.Println(b)
}
同樣,函數的參數順序也屬簽名組成部分。
func main() {
	var a func(int, string)
	var b func(string, int)
	b = a // 錯誤:cannot use a (type func(int, string)) as type func(string, int) in assignment
	b("s", 1)
}
未命名類型轉換規則:
所屬類型相同;
基礎類型相同,且其中一個是未命名類型
數據類型相同,將雙向通道賦值給單向通道,且其中一個為未命名類型;
將默認值 nil 賦值給 切片、字典、通道、指針、函數 或 接口;
對象實現了目標接口;
func main() {
	type data [2]int
	var d data = [2]int{1, 2} // 基礎類型相同,右值為 未命名類型
	fmt.Println(d)
	a := make(chan int, 2)
	var b chan<- int = a // 雙向通道 轉換為 單向通道,其中 b 為 未命名類型
	b <- 2
}
                            總結
以上是生活随笔為你收集整理的《Go学习笔记 . 雨痕》类型的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: ruby -- 基础学习(五)empty
 - 下一篇: vue offsetTop顶部距离