1. 程式人生 > >Go語言--基礎語法筆記

Go語言--基礎語法筆記

###

換了工作,好久沒有新增新文章了,本來是想更新到github上的,想想還是在部落格裡放著,感覺以前的文章都沒有很仔細,都只是問題處理的記錄,

以後想新加一些整理的筆記也好

###

 

主要內容

2.1變數

2.2資料型別

2.3資料型別相互轉換

2.4指標

2.5變數生命期

2.6字串應用

2.7常量 2.8類型別名

2.1變數

2.1.1 宣告變數

功能:儲存使用者的資料

注意: 變數必須經過宣告才能開始使用

變數宣告格式:

  1. 標準格式

var 變數名 變數型別

以關鍵字var開頭,後置變數型別,行尾無線分號

package main
import ("fmt")
func main() {
     var a int
     var b string
     var c []float32
     var d func() bool
     var e struct{
        x int
     }
} 
  1. 批量格式

使用var關鍵字 和括號

package main
import ("fmt")
func main() {
     var (
        a int
        b string
        c []float32
        d func() bool
        e struct{
            x int
        }
     )
}

  

2.1.2 初始化變數

  • 整型和浮點型變數預設值:0

  • 字串變數的預設值空字串

  • 布林型預設值為bool

  • 切片、函式、指標變數的預設值為nil

1.標準格式

var 變數名 變數型別 = 表示式

var hp int = 100

2.編譯器推導的型別

var hp1 = 100

標準格式基礎上,省略int,編譯器推導

var attack = 40
var defence = 20
var damageRate float32 = 0.17
var damage = float32(attack-defence) * damageRate
fmt.Println(damage)
// 3.4

  

3.短變數宣告並初始化

hp := 100

注意:變數已經被宣告過了,再次宣告並賦值,使用短變數宣告會編譯報錯

var p string
p := '123'
fmt.Println(p)
// 錯誤資訊:no new variables on left side of :=(44.4)
// var p 聲明瞭p變數, p := '123' 會再次宣告並賦值
hp3 := 50
fmt.Println(hp3)

  

2.1.3 多個變數同時賦值

順序:從左到右

var a int = 100
var b int = 200
​
a, b = b, a
fmt.Println(a, b) //200 100

  

2.1.4 匿名變數

匿名變數用一個"_"下滑線表示

只需要在變數宣告的地方,用下劃線代替即可

package main
import ("fmt")
func main() {
    a1, _ := getData()
    _, b1 := getData()
    fmt.Println(a1, b1)
}
​
type IntSlice []int
// 編寫一個len方法,提供切片的長度
func (p IntSlice) Len() int { return len(p)}
// 根據提供i,j元素索引,兩個元素進行比較,返回比較結果
func (p IntSlice) Less(i, j int) bool { return p[i] < p[j]}
// 根據提供i,j元素索引,交換兩個元素的值
func (p IntSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i]}
​
func getData() (int, int) {
    return 200, 100
}

  

2.2 資料型別

2.2.1 整型

兩種: 長度 int8 int16 int32 int64 無符號 uint8 uint16 uint32 uint64

2.2.2 浮點型

兩種:float32和float64

fmt.Println("hello world")
fmt.Printf("%f\n", math.Pi)
fmt.Printf("%.2f\n", math.Pi)

  

2.2.3 布林型

注意:go不允許將整型型別轉換為布林型, 無法參與數值運算,也無法跟其他型別進行轉換

var n bool
fmt.Println( int(n) * 2 ) 
//cannot convert n (type bool) to type int

  

2.2.4 字串

用雙引號括起來的內容就是為字串的內容

  str := "hello world string"
  ch := "中文"
  fmt.Println(str, ch)
    //hello world string 中文

  

  • 字串轉義符

    符合 說明
    \r 回車
    \n 換行符
    \t 製表符
    ' 單引號
    " 雙引號
    \ 雙斜槓
    fmt.Println( "str := \"c:\\Go\\bin\\go.exe\" ")
    str := "c:\Go\bin\go.exe" 
    

      

  • 字串實現基於utf-8編碼

  • 定義多行字串

const longstr = `
    第一行
    第二行
    \r\n
    。。。
    
    `
    fmt.Println(longstr)
    
    //第一行
    //第二行
    //\r\n
    //。。。
​
    const codeTemplate = ` // Generated by github.com/davyxu/cellnet/protoc-gen-msg
    // DO NOT EDIT!{{range.Protos}}
    // Source:{{.Name}} {{end}}
    
    package {{.PackageName}}
    {{if gt .TotalMessages 0}}
    import (
        "github.com/davyu/cellnet"
        "reflect"
        _ "github.com/davyxu/cellnet/codec/pb"
    )
    {{end}}
    
    func init() {
        {{range .Protos}}
        //{{.Name}}{{range .Messages}}
        cellnet.RegisterMessageMeta("pb", "{{.FullName}}")
        reflect.TypeOf((*{{.Name}})(nil)).Elem(), {{.MsgID}}) {{end}}
        {{end}}
    }
    `
    fmt.Println(codeTemplate)

  


2.2.5 字元

字串中每一個元素都叫字元 兩種: 一種為uint8型別 或是 byte型別 代表ascll碼的一個字元 另一種為rune型別, 代表一個UTF-8型別,實際為int32 用於處理中文、日文等複合字符 使用fmt.Printf 中“%T”輸出變數實際型別,可用於檢視byte和rune型別

var a byte = 'a'
fmt.Printf("%d %T\n", a, a)
var b rune = '中'
fmt.Printf("%d %T\n", b, b)
//97 uint8
//20013 int32

  

2.2.7 切片 -- 能動態分配的空間

一個擁有相同型別元素的可變長的序列

var name []T

T 代表切片元素型別, 即可以整型、浮點型、布林型、切片、map、函式等

t := make([]int, 5)
t[0] = 1
t[1] = 2
t[3] = 3
fmt.Println(t)
//[1 2 0 3 0]
    
str1 := "hello world"
fmt.Println(str1[6:])
//world

  

2.3 轉換不同的資料型別

格式:

T(表示式) 輸出各數值範圍

package main
​
import (
    "fmt"
    "math"
)
​
func main() {   
    fmt.Println("int8 range:", math.MinInt8, math.MaxInt8)
    fmt.Println("int16 range:", math.MinInt16, math.MaxInt16)
    fmt.Println("int32 range:", math.MinInt32, math.MaxInt32)
    fmt.Println("int64 range:", math.MinInt64, math.MaxInt64)
    int8 range: -128 127
    int16 range: -32768 32767
    int32 range: -2147483648 2147483647
    int64 range: -9223372036854775808 9223372036854775807
​
    //初始化一個32位整型值
    var a1 int32 = 1047483647
    fmt.Printf("int32: 0x%x %d\n", a1, a1)
    //nt32: 0x3e6f54ff 1047483647
    //int32 轉為 int16, 發生數值截斷
    b1 := int16(a1)
    fmt.Printf("a1 int16: 0x%x %d\n", b1, b1)
    //a1 int16: 0x54ff 21759
    
    //將常量儲存為float32型別
    var c float32 = math.Pi
    //轉為int型別,浮點數發生精度丟失
    fmt.Println(int(c))
    //3
}

  

2.4 指標

兩個核心: 一種是型別指標,允許對這個指標型別的資料進行修改。 傳遞資料使用使用指標,而無須拷貝資料 型別指標不能進行偏移和運算 二種是切片, 由指向起始元素的原始指標、元素數量和容量組成

2.4.1 認識指標地址和指標型別

每個變數在執行時都會被記憶體分配一個地址,這個地址代表變數在記憶體中的位置 使用“&”操作符放在變數前面對變數進行“取地址”操作 格式:

     ptr := &variable   //variable的型別為T

其中v代表被取地址的變數,被取地址的variable使用ptr變數進行接收,ptr的型別為“T”,稱作T的指標型別。“”代表指標

package main
​
import (
    "fmt"
    "math"
)
​
func main() {   
    var cat int = 1
    var str2 string = "banana"
    fmt.Printf("%p, %p\n", &cat, &str2)
}
    //0xc00004e0e8, 0xc0000421f0 為cat,str2取地址後的指標值

  

注意:變數、指標和地址三種的關係是:每個變數都擁有地址,指標的值就是地址

2.4.2 從指標獲取指標指向的值

對變數“&”取地址操作後獲得這個變數的指標,對指標使用“*”操作,就是指標取值

package main
​
import (
    "fmt"
    "math"
)
func main() {
    var house = "Malibu Point 10880, 90265"
    // 對字串取地址,ptr1型別為*string
    ptr1 := &house
    // 列印ptr型別
    fmt.Printf("ptr1 型別:%T\n", ptr1)
    // 列印ptr指標地址
    fmt.Printf("ptr1 地址:%p\n", ptr1)
    // 對指標進行取值操作
    value := *ptr1
    // 取值後型別
    fmt.Printf("value 型別:%T\n", value)
    // value值
    fmt.Printf("value:%s\n", value)
}
// ptr1 型別:*string
// ptr1 地址:0xc000042200
// value 型別:string
// value:Malibu Point 10880, 90265

總結:

取地址“&”和取值“”是一對互補操作符,“&”取地址,"&"根據地址取出地址指向的值 1.對變數進行其地址(&)操作,可獲得這個變數的指標變數 2.指標變數的值是指標地址 3.對指標變數進行取值()操作,可以獲得指標變數指向的原變數的值

2.4.3 使用指標修改值

x, y := 1,2

package main
​
import (
    "fmt"
    "math"
)
​
func main() {
    //錯誤示例
    swap1(&x, &y)
    fmt.Println("x: ",x, "y:", y)
    //x:  1 y:  2
    //正確
    swap(&x, &y)
    fmt.Println("x: ", x, "y: ",y)
    //x:  2 y:  1
}
// 交換函式
func swap(a, b *int) {
    // 取a的指標的值,賦給臨時變數t
    t := *a
    //取b指標的值,賦值給a指標指向的變數
    *a = *b
    //a指標的值賦值給b指標指向的變數
    *b = t
}
​
// 錯誤示例
func swap1(a, b *int) {
    b, a = a, b
}

  

2.4.5 建立指標的另一種方法--new()函式

new(型別)

str3 := new(string)
*str3 = "ninja"
fmt.Println(*str3)
fmt.Println(str3)
//ninja
//0xc000042230

2.6 字串應用

2.6.1 計算機字串長度 -- len()

go 語言字串都是以UTF-8格式儲存,每個中文佔用3個字元

tip1 := "genji is a ninja"
fmt.Println(len(tip1))
// 16
tip2 := "忍者無敵"
fmt.Println(len(tip2))
//12
// 使用RuneCountInString()統計Uncode字元數量
fmt.Println(utf8.RuneCountInString("忍者"))

 

總結

  • ASCII字串長度使用len()函式

  • Unicode字串長度使用utf8.RuneCountInString()函式

 

2.6.2 遍歷字串 -- 獲取每個字串

兩種寫法

  1. 遍歷每一ASCII字元, 使用for迴圈遍歷

theme := "阻擊 start"
​
    for i := 0; i < len(theme); i++ {
​
        fmt.Printf("ascii: %c %d\n", theme[i], theme[i])
​
    }
    // ascii: é 233
    // ascii: • 152
    // ascii: » 187
    // ascii: å 229
    // ascii: • 135
    // ascii: » 187
    // ascii:   32
    // ascii: s 115
    // ascii: t 116
    // ascii: a 97
    // ascii: r 114

  

  1. 按Unicode字元遍歷字串  

  for _, s := range theme {
​
        fmt.Printf("Unicode %c %d\n", s, s)
​
    }
​
    // Unicode 阻 38459
    // Unicode 擊 20987
    // Unicode   32
    // Unicode s 115
    // Unicode t 116
    // Unicode a 97
    // Unicode r 114
    // Unicode t 116

  

總結:

  • ASCII字串遍歷直接使用下標

  • Unicode字串遍歷使用for range

2.6.3 獲取字串的某一段字元

string.Index() 在字串中搜索另一個子串

tracer := "努力擁抱每一天,不斷成長"
comma := strings.Index(tracer, "每一天")
posi := strings.Index(tracer[comma:], "成長")
​
fmt.Println(comma, posi, tracer[comma+posi:])
​
 // 12 18 成長

 

總結:

  • strings.Index:正向搜尋子字串

  • string.LastIndex: 反向搜尋自字串

搜尋的起始位置可以通過切片偏移製作

2.6.4 修改字串

go語言無法直接修改每一個字元元素,只能通過重新構造新的字串並賦值給原來的字串變數

angel := "Hero nerver die"
​
arrayBytes := []byte(angel)
​
for i := 5; i <= 10; i++ {
​
     arrayBytes[i] = '-'
​
}
​
fmt.Println(arrayBytes)
​
// [72 101 114 111 32 45 45 45 45 45 45 32 100 105 101]
​
fmt.Println(string(arrayBytes))
​
 // Hero ------ die

  

總結

  • Go語言的字串是不可以改變的

  • 修改字串時,可以將字串轉換為[]byte進行修改

  • []byte 和string 可以通過強制型別轉換互換

 

2.6.5 連線字串

可以使用加號“+”連線 可以使用類似於StringBuilder的機制連線,更高效

hamer := "GO GO GO"
sickle := "You Can"
​
// 宣告位元組緩衝
​
var stringBuilder bytes.Buffer
​
// 將字串寫入緩衝區
​
stringBuilder.WriteString(hamer)
​
stringBuilder.WriteString(sickle)
​
//將緩衝以字串形式輸出
​
fmt.Println(stringBuilder.String())
​
// GO GO GOYou Can

  

  • bytes.Buffer可以緩衝並寫入各種位元組陣列,字串也是一種字串陣列,使用writeString()

  • 將需要連線的字串,通過bytes.Buffer宣告緩衝stringBuilder呼叫WriteString()方法寫入裡面,

  • 再通過stringBuilder.String()方法將緩衝轉換為字串

2.6.6 格式化

寫法: fmt.Sprintf(格式化樣式,引數列表) 格式化樣式:字串形式,格式化動詞以%開頭 引數列表:多個引數以逗號分隔,個數必須與格式化中樣式個數一一對應

 var progress = 2
    var target = 8
​
    // 兩引數格式化
    title := fmt.Sprintf("以完成%d個任務,還差%d個就完成", progress, target)
​
    fmt.Println(title)
    // 以完成2個任務,還差8個就完成
​
    pi := math.Pi
​
    // 按數值本身格式輸出
    variant := fmt.Sprintf("%v %v %v", "月球基地", pi, true)    
    fmt.Println(variant)
    // 月球基地 3.141592653589793 true
​
    profile := &struct {
        Name string
        HP   int
    }{
        Name: "stat",
        HP: 150,
    }
​
    fmt.Printf("使用'%%+v' %+v\n", profile)
    fmt.Printf("使用'%%#v' %#v\n", profile)
    fmt.Printf("使用'%%T' %T\n", profile)
​
    // 使用'%+v' &{Name:stat HP:150}
    // 使用'%#v' &struct { Name string; HP int }{Name:"stat", HP:150}
    // 使用'%T' *struct { Name string; HP int }

  

base64編碼解碼示例

package main
​
import (
    "fmt"
    "encoding/base64"
)
​
func main() {
​
    // 需要處理的字串
    message := "Away from keyboard. https://golang.org/" 
​
    // 編碼訊息, 傳入的字串需轉為位元組陣列,才能供這個函式使用
    encodeMessage := base64.StdEncoding.EncodeToString([]byte(message))
    // 輸出編碼完成的訊息
    fmt.Println(encodeMessage)
    // 解碼訊息
    data, err := base64.StdEncoding.DecodeString(encodeMessage)
    // 出錯處理
​
    if err != nil {
        fmt.Println(err)
    } else {
        fmt.Println(string(data))
    }
​
    // QXdheSBmcm9tIGtleWJvYXJkLiBodHRwczovL2dvbGFuZy5vcmcv
    // Away from keyboard. https://golang.org/
​
​
}