尹成學院golang學習快速筆記(1)型別

尹成發表於2018-05-26

1.1 變數

 Go 是靜態型別語⾔,不能在運⾏期改變變數型別。

 使⽤關鍵字 var 定義變數,⾃動初始化為零值。如果提供初始化值,可省略變數型別,由

編譯器⾃動推斷。

var x int
var f float32 = 1.6
var s = "abc"

在函式內部,可⽤更簡略的 ":=" ⽅式定義變數。

 

func main() {

x := 123

// 注意檢查,是定義新區域性變數,還是修改全域性變數。該⽅式容易造成錯誤。

}

 

可⼀次定義多個變數。

var x, y, z int

var s, n = "abc", 123

var (

a int

b float32

)

func main() {

n, s := 0x1234, "Hello, World!"

println(x, s, n)

}

 


多變數賦值時,先計算所有相關值,然後再從左到右依次賦值。

data, i := [3]int{0, 1, 2}, 0

i, data[i] = 2, 100+l

// (i = 0) -> (i = 2), (data[0] = 100)

特殊只寫變數 "_",⽤於忽略值佔位。

func test() (int, string) {

return 1, "abc"

}

func main() {

_, s := test()

println(s)

}

 

編譯器會將未使⽤的區域性變數當做錯誤。


var s string

func main() {

i := 0

// 全域性變數沒問題。

// Error: i declared and not used。(可使⽤ "_ = i" 規避)

}

 

注意重新賦值與定義新同名變數的區別。

 

s := "abc"

println(&s)

s, y := "hello", 20

println(&s, y)

{

s, z := 1000, 30

println(&s, z)


// 重新賦值: 與前 s 在同⼀層次的程式碼塊中,且有新的變數被定義。

// 通常函式多返回值 err 會被重複使⽤。

// 定義新同名變數: 不在同⼀層次程式碼塊。

}

輸出:

0x2210230f30

0x2210230f30 20

0x2210230f18 30

1.2 常量

常量值必須是編譯期可確定的數字、字串、布林值。

const x, y int = 1, 2

const s = "Hello, World!"

const (

// 多常量初始化

// 型別推斷

// 常量組

a, b

c= 10, 100

bool = false

)

func main() {

const x = "xxx"

// 未使⽤區域性常量不會引發編譯錯誤。

}

不⽀持 1UL2LL 這樣的型別字尾。

在常量組中,如不提供型別和初始化值,那麼視作與上⼀常量相同。

const (

s= "abc"

x// x = "abc"

)

 

 

常量值還可以是 lencapunsafe.Sizeof 等編譯期可確定結果的函式返回值。

 

 

const (

a= "abc"

b= len(a)

c= unsafe.Sizeof(b)

)

 

 

如果常量型別⾜以儲存初始化值,那麼不會引發溢位錯誤。

 

const (

a byte = 100

// int to byte

b int= 1e20

// float64 to int, overflows

)

 

 

 

列舉

 

關鍵字 iota 定義常量組中從 0 開始按⾏計數的⾃增列舉值。

 

const (

Sunday = iota  // 0

Monday  // 1,通常省略後續⾏表示式。

Tuesday  // 2

Wednesday  // 3

Thursday  // 4

Friday  // 5

Saturday // 6

)
const (
_
KB= iota
MB
GB
TB

int64 = 1 << (10 * iota)

// iota = 0

// iota = 1

// 與 KB 表示式相同,但 iota = 2

)

在同⼀常量組中,可以提供多個 iota,它們各⾃增⻓。

 

const (

A, B = iota, iota << 10

C, D

// 0, 0 << 10

// 1, 1 << 10

)

如果 iota ⾃增被打斷,須顯式恢復。

 

 

const (

A= iota// 0

B= "c" // 1

C= iota// c

D // c,與上⼀⾏相同。

E // 4,顯式恢復。注意計數包含了 C、D 兩⾏。

F// 5

)

 

 

可通過⾃定義型別來實現列舉型別限制。

 

type Color int

const (

Black Color = iota

Red

Blue

)

 

func test(c Color) {}

func main() {

c := Black

test(c)

x := 1

test(x) // Error: cannot use x (type int) as type Color in function argument


test(1) // 常量會被編譯器⾃動轉換。


}




型別

⻓度

預設值

說明

bool

1

false

 

byte

1

0

uint8

rune

4

0

Unicode Code Point, int32

int, uint

4  8

0

32  64 

int8, uint8

1

0

-128 ~ 127, 0 ~ 255

int16, uint16

2

0

-32768 ~ 32767, 0 ~ 65535

int32, uint32

4

0

-21 ~ 21 , 0 ~ 42 

int64, uint64

8

0

 

float32

4

0.0

 

float64

8

0.0

 

complex64

8

 

 

complex128

16

 

 

uintptr

4  8

 

⾜以儲存指標的 uint32  uint64 整數

array

 

 

值型別

struct

 

 

值型別

string

 

""

UTF-8 字串

slice

 

nil

引⽤型別

map

 

nil

引⽤型別

channel

 

nil

引⽤型別

interface

 

nil

接⼝

function

 

nil

函式




 

 

 

1.3 基本型別

 

明確字型別命名,⽀持 Unicode,⽀持常⽤資料結構。

⽀持⼋進位制、⼗六進位制,以及科學記數法。標準庫 math 定義了各數字型別取值範圍。

a, b, c, d := 071, 0x1F, 1e9, math.MinInt16

空指標值 nil,⽽⾮ C/C++ NULL


1.4 引⽤型別

引⽤型別包括 slicemap  channel。它們有複雜的內部結構,除了申請記憶體外,還需

要初始化相關屬性。

內建函式 new 計算型別⼤⼩,為其分配零值記憶體,返回指標。⽽ make 會被編譯器翻譯

成具體的建立函式,由其分配記憶體和初始化成員結構,返回物件⽽⾮指標。


 

a := []int{0, 0, 0}
a[1] = 10
b := make([]int, 3)
b[1] = 10
c := new([]int)
c[1] = 10

// 提供初始化表示式。
// makeslice
// Error: invalid operation: c[1] (index of type *[]int)


 


1.5 型別轉換

不⽀持隱式型別轉換,即便是從窄向寬轉換也不⾏。

var b byte = 100

// var n int = b

var n int = int(b)

// Error: cannot use b (type byte) as type int in assignment

// 顯式轉換


使⽤括號避免優先順序錯誤。

 


*Point(p)

(*Point)(p)

<-chan int(c)

(<-chan int)(c)

// 相當於 *(Point(p))

// 相當於 <-(chan int(c))


同樣不能將其他型別當 bool 值使⽤。

 

a := 100

if a {

println("true")

// Error: non-bool a (type int) used as if condition

}

 


1.6 字串

 

字串是不可變值型別,內部⽤指標指向 UTF-8 位元組陣列。


 預設值是空字串 ""

 ⽤索引號訪問某位元組,如 s[i]

 不能⽤序號獲取位元組元素指標,&s[i] ⾮法。

 不可變型別,⽆法修改位元組陣列。

 位元組陣列尾部不包含 NULL

 

//runtime.h

struct String

{



byte*

intgo

str;

len;

};


使⽤索引號問字元 (byte)

s := "abc"

println(s[0] == '\x61', s[1] == 'b', s[2] == 0x63)

輸出:

true true true

 

 

使⽤ "`" 定義不做轉義處理的原始字串,⽀持跨⾏。


s := `a

b\r\n\x00

c`

 

println(s)

輸出:

a

b\r\n\x00

c

 

 

連線跨⾏字串時,"+" 必須在上⼀⾏末尾,否則導致編譯錯誤。

 

s := "Hello, " +

"World!"

s2 := "Hello, "

+ "World!"

// Error: invalid operation: + untyped string


⽀持⽤兩個索引號返回⼦串。⼦串依然指向原位元組陣列,僅修改了指標和⻓度屬性。

 

 

s := "Hello, World!"

s1 := s[:5]
// Hello

s2 := s[7:]

s3 := s[1:5]

// Hello

// World!

// ello


 

 


單引號字元常量表⽰ Unicode Code Point,⽀持 \uFFFF\U7FFFFFFF\xFF 格式。

對應 rune 型別,UCS-4

 

 

func main() {

fmt.Printf("%T\n", 'a')

 

var c1, c2 rune = '\u6211', '們'

println(c1 == '我', string(c2) == "\xe4\xbb\xac")

}

輸出:

int32

// rune  int32 的別名

true true

 

 

要修改字串,可先將其轉換成 []rune  []byte,完成後再轉換為 string。⽆論哪種轉

換,都會重新分配記憶體,並複製位元組陣列。

 

func main() {

s := "abcd"

bs := []byte(s)

bs[1] = 'B'

println(string(bs))

u := "電腦"

us := []rune(u)

us[1] = '話'

println(string(us))

}

輸出:

aBcd

電話

 

 

 for 迴圈遍歷字串時,也有 byte  rune 兩種⽅式。

 

 

func main() {

s := "abc漢字"

for i := 0; i < len(s); i++ {

fmt.Printf("%c,", s[i])

}

fmt.Println()

for _, r := range s {

fmt.Printf("%c,", r)

}

// byte

// rune

}

輸出:

a,b,c,æ,±,,å,­,,

a,b,c,,,

 

 

 

1.7 指標

 

⽀持指標型別 *T,指標的指標 **T,以及包含包名字首的 *<package>.T

 

 

 預設值 nil,沒有 NULL 常量。

 操作符 "&" 取變數地址,"*" 透過指標訪問⺫標物件。

 不⽀持指標運算,不⽀持 "->" 運算子,直接⽤ "." 訪問⺫標成員。

 

func main() {

type data struct{ a int }

var d = data{1234}

var p *data

p = &d

fmt.Printf("%p, %v\n", p, p.a)

}

輸出:

0x2101ef018, 1234

 

 

不能對指標做加減法等運算。

 

 

x := 1234

p := &x

// 直接⽤指標訪問⺫標物件成員,⽆須轉換。


 


p++


// Error: invalid operation: p += 1 (mismatched types *int and int)


可以在 unsafe.Pointer 和任意型別指標間進⾏轉換。

 

func main() {

x := 0x12345678

p := unsafe.Pointer(&x)

n := (*[4]byte)(p)

for i := 0; i < len(n); i++ {

fmt.Printf("%X ", n[i])

}

// *int -> Pointer

// Pointer -> *[4]byte

}

輸出:

78 56 34 12

 

 

返回區域性變數指標是安全的,編譯器會根據需要將其分配在 GC Heap 上。

 

func test() *int {

x := 100

return &x


// 在堆上分配 x 記憶體。但在內聯時,也可能直接分配在⺫標棧。



}

 

 

 Pointer 轉換成 uintptr,可變相實現指標運算。

 

 

func main() {

d := struct {

s

x


string

int

}{"abc", 100}

p := uintptr(unsafe.Pointer(&d))

p += unsafe.Offsetof(d.x)

// *struct -> Pointer -> uintptr

// uintptr + offset

p2 := unsafe.Pointer(p)

px := (*int)(p2)

*px = 200

fmt.Printf("%#v\n", d)

// uintptr -> Pointer

// Pointer -> *int

// d.x = 200

}

輸出:

struct { s string; x int }{s:"abc", x:200}

注意:GC  uintptr 當成普通整數物件,它⽆法阻⽌ "關聯" 物件被回收。


1.8 ⾃定義型別


可將型別分為命名和未命名兩⼤類。命名型別包括 boolintstring 等,⽽ array

slicemap 等和具體元素型別、⻓度等有關,屬於未命名型別。

具有相同宣告的未命名型別被視為同⼀型別。

 

 具有相同基型別的指標。

 具有相同元素型別和⻓度的 array

 具有相同元素型別的 slice

 具有相同鍵值型別的 map

 具有相同元素型別和傳送⽅向的 channel

 具有相同欄位序列 (欄位名、型別、標籤、順序) 的匿名 struct

 簽名相同 (引數和返回值,不包括引數名稱)  function

 ⽅法集相同 (⽅法名、⽅法簽名相同,和次序⽆關)  interface

 

var a struct { x int `a` }

var b struct { x int `ab` }

 

// cannot use a (type struct { x int "a" }) as type struct { x int "ab" } in assignment

b = a

 

 

可⽤ type 在全域性或函式內定義新型別。

 

func main() {

type bigint int64

 

var x bigint = 100

println(x)

}



新型別不是原型別的別名,除擁有相同資料儲存結構外,它們之間沒有任何關係,不會持

有原型別任何資訊。除⾮⺫標型別是未命名型別,否則必須顯式轉換。

 

x := 1234

var b bigint = bigint(x)

var b2 int64 = int64(b)

var s myslice = []int{1, 2, 3}

var s2 []int = s

// 必須顯式轉換,除⾮是常量。

// 未命名型別,隱式轉換。

 


 


test(1) // 常量會被編譯器⾃動轉換。







網址:http://www.qukuailianxueyuan.io/



欲領取造幣技術與全套虛擬機器資料

區塊鏈技術交流QQ群:756146052  備註:CSDN

尹成學院微信:備註:CSDN


 

 


 


test(1) // 常量會被編譯器⾃動轉換。




網址:http://www.qukuailianxueyuan.io/



欲領取造幣技術與全套虛擬機器資料

區塊鏈技術交流QQ群:756146052  備註:CSDN

尹成學院微信:備註:CSDN

相關文章