主頁 > 後端開發 > 【Go語言學習筆記一】基礎語法

【Go語言學習筆記一】基礎語法

2020-12-04 06:36:00 後端開發

Go學習筆記

Go語言基礎語法

行分隔行

在 Go 程式中,一行代表一個陳述句結束,每個陳述句不需要像 C 家族中的其它語言一樣以分號 ; 結尾,

但是可以使用分號;結尾,如果當你將多個陳述句寫在同一行時,則必須使用分號;

一、變數

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
    }
} 

代碼說明:

  • 第3行,宣告一個整型型別的變數,可以保存整數數值,

  • 第4行,宣告一個字串型別的變數,

  • 第5行,宣告一個 32 位浮點切片型別的變數,浮點切片表示由多個浮點型別組成的資料結構,

  • 第6行,宣告一個回傳值為布爾型別的函式變數,這種形式一般用于回呼函式,即將函式以變數的形式保存下來,在需要的時候重新呼叫這個函式,

  • 第7行,宣告一個結構體型別的變數,這個結構體擁有一個整型的 x 欄位,

使用var關鍵字和括號批量宣告(推薦

var (
   a int
   b string
   c []float32
   d  func () bool
   e  struct {
     x int
   }
)

使用關鍵字var和括號,可以將一組變數定義放在一起, 

1.2 初始化變數

變數的宣告可以包含初始值,每一個變數對應一個值,

如果初始化值已存在,則可以省略型別;變數會從初始值中獲得型別,

變數初始化的標準格式:

var 變數名 型別 = 運算式

簡化形式:

var 變數名 = 運算式

例如:

var x int = 100

可以寫成:

var x = 100

默認值:

沒有明確初始值的變數宣告會被賦予它們一個默認值:

整型和浮點型變數的默認值為 0,
字串變數的默認值為空字串,
布爾型變數默認為 bool,
切片、函式、指標變數的默認為 nil,

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

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

注意:由于使用了 :=,而不是賦值的 =,因此推導宣告寫法的左值變數必須是沒有定義過的變數,若定義過,將會發生編譯錯誤,

注意:在多個短變數宣告和賦值中,至少有一個新宣告的變數出現在左值中,即便其他變數名可能是重復宣告的,編譯器也不會報錯,例如:

x, z := a, b
y, z := a, b

1.4 多個變數同時賦值

使用Go的“多重賦值特性”,可以輕松完成變數交換的任務,

package` `main
import` `"fmt"
func main() {
   var a = 100
   var b = 200
   a, b = b, ago
   fmt.Println(a, b)
}

多重賦值時,變數的左值和右值按從左到右的順序賦值,

1.5 匿名變數(_)

在使用多重賦值時,如果不需要在左值中接收變數,可以使用匿名變數,

匿名變數用一個下劃線 _ 來表示,使用匿名變數時,只需要在變數宣告的地方使用下劃線替換即可,例如:

var a int
a, _ = 100, 200

注意:匿名變數不占用命名空間,不會分配記憶體,匿名變數與匿名變數之間也不會因為多次宣告而無法使用,

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
}

二、常量

常量是恒定不變的值,例如圓周率,

常量的宣告與變數類似,只不過是使用 const 關鍵字,

常量可以是字符、字串、布林值和數值,

常量不能用 := 語法宣告,

常量的宣告, 例如:

const pi = 3.1415926

注意:常量在宣告的時候必須賦值,

多個變數可以一起宣告,類似的,多個變數也可以一起宣告,例如:

const(
    pi = 3.1415926
    e = 2.718281
)

三、基本資料型別

Go語言中有豐富的資料型別,除了基本的整型、浮點型、布爾型、字串外,還有切片、結構體、函式、map、通道(channel)等,

Go 語言的基本型別和其他語言大同小異,

3.1 整型

整型可以分成以下兩個大類:

按長度分為:int8、int16、int32、int64

對應的無符號整型:uint8、uint16、uint32、uint64

其中,uint8 就是我們熟知的 byte 型.

3.2 浮點型

Go語言支持兩種浮點型數:

float32、float64.

注意:沒有float

Go語言的浮點型默認宣告為float64.

3.3 布爾型

布爾型資料只有 true(真)和 false(假)兩個值,

注意:

在Go語言中,
true和false均為小寫
不允許將整型強制轉換為布爾型

3.4 字串

字串的兩種表示形式:

\1. 雙引號,會識別轉義字符

\2. 反引號,不會識別轉義字符,以字串的原生形式輸出,包括換行和特殊字符,

3.4.1 字串常見轉義符

轉義符 含義
\r 回車符(回傳行首)
\n 換行符
\t 制表符
' 單引號
" 雙引號
\ 反斜杠

3.4.2 反引號定義多行字串

const str = ` 第一行
第二行
第三行
\r\n`
fmt.Println(str)

代碼運行結果:

第一行
第二行
第三行
\r\n

3.5 字符

字串中的每一個元素叫做“字符”,在遍歷或者單個獲取字串元素時可以獲得字符,

Go語言的字符有以下兩種:

  • 一種是 uint8 型別,或者叫 byte 型,代表了 ASCII碼的一個字符,
  • 另一種是 rune 型別,代表一個 UTF-8 字符,當需要處理中文、日文或者其他復合字符時,則需要用到 rune 型別,rune 型別實際是一個 int32,

四、資料型別的轉換

Go語言使用型別前置加括號的方式進行型別轉換,一般格式如下:

T(運算式)

其中,T 代表要轉換的型別,運算式包括變數、復雜算子和函式回傳值等,

注意:在型別轉換時,需要考慮兩種型別的關系和范圍,是否會發生數值截斷等,

package main
 
import "fmt"
 
func main(){
    var n1 int = 20
    var n2 float64 = float64(n1)
 
    n2 = n2 + 3.6
 
    var n3 int32 = int32(n2) // 當將一個float型別轉成 int時,是直接去掉小數點后的部分
 
    fmt.Printf("n1 type=%T, val=%v; n2 type=%T, val=%v; n3 type=%T, val=%v\n",
        n1, n1, n2, n2, n3, n3)
}

代碼運行結果:

n1 type=int, val=20; n2 type=float64, val=23.6; n3 type=int32, val=23

五 、指標

兩個核心:

一種是型別指標,允許對這個指標型別的資料進行修改, 傳遞資料使用使用指標,而無須拷貝資料 型別指標不能進行偏移和運算

二種是切片, 由指向起始元素的原始指標、元素數量和容量組成

5.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取地址后的指標值

  

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

5.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.對指標變數進行取值()操作,可以獲得指標變數指向的原變數的值

5.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
}

  

5.4 創建指標的另一種方法--new()函式

new(型別)

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

六、字串應用

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()函式

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

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: 反向搜索自字串

搜索的起始位置可以通過切片偏移制作

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 可以通過強制型別轉換互換

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()方法將緩沖轉換為字串

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/


}

  

七、列舉

7.1 列舉 -- 一組常量值

// 使用iota模擬列舉
  type Weapon int
   const (
       Arrow Weapon = iota // 開始生成列舉值,默認為0
        Shuriken
       SniperRifle
       Rifle
       Blower
   )
   // 輸出所有列舉值
    fmt.Println(Arrow, Shuriken, SniperRifle, Rifle, Blower)
   var weapon Weapon = Blower
    fmt.Println(weapon)
    // 0 1 2 3 4
    // 4

7.2 列舉--將列舉值轉換為字串

package main
import ("fmt")

// 宣告芯片型別
type ChipType int

const (
    None ChipType = iota
    GPU
    CPU
)

func (c ChipType) String() string {

    switch c {
    case None:
        return "None"
    case GPU:
        return "GPU"
    case CPU:
    return "CPU"

     }
    return "N/A"

}

func main() {
    // 輸出CPU的值并以整型格式顯示
    fmt.Printf("%s %d", CPU, CPU)
    //CPU 2
}

八、 型別別名

8.1 區分型別別名與型別定義

型別別名的寫法:

type TypeAlias = Type

型別別名規定:

TypeAlias只是Type的別名,本質上TypeAlias與Type是同一個型別

// 將NewInt定義為int型別
type NewInt int
// 將int取一個別名叫IntAlias
type IntAlias = int

// 將a宣告為一個NewInt型別
var alias_a NewInt
fmt.Printf("a type: %T\n", alias_a)
// a type: main.NewInt

// 將a2宣告為IntAlias型別
var alias_a2 IntAlias
fmt.Printf("a2 type: %T\n", alias_a2)
 // a2 type: int 

8.2 非本地型別不能定義方法

不能為不在同一個包中宣告的型別定義方法,即不能為在其他包宣告的型別在本地包中定義方法

package main

import ("time")

// 2.8.2
// 定義time.Duration 的別名為MyDuration
type MyDuration = time.Duration

// 為MyDuration 添加一個函式
func (m MyDuration) EasySet(a String) {
    
}

func main() {
    
}
//# 2-base/2.2-data_type
//.\data_type.go:51:6: cannot define new methods on non-local type time.Duration
//.\data_type.go:51:31: undefined: String
//exit status 2
//Process exiting with code: 1

8.3 在結構體成員嵌入時使用別名

package main

import (
    "fmt"
    "reflect"
)

// 定義商標結構
type Brand struct {

}
// 為商標結構添加Show()方法
func (t Brand) Show() {

}

// 為Brand定義一個別名
type FakeBrand = Brand
// 定義車輛結構,嵌入商標結構
type  Vehicle struct {
    Brand
    FakeBrand
}

func main() {

    // 宣告變數 a 為車輛型別
    var a Vehicle
    // 指定呼叫FakeBrand的Show
    a.FakeBrand.Show()
    // 取a的型別反射物件
    ta := reflect.TypeOf(a)

    // 遍歷a的所有成員
    for i := 0; i < ta.NumField(); i++ {
        // ta 成員資訊
        f := ta.Field(i)
        // 列印成員的欄位名和型別
        fmt.Printf("FieldName: %v, FieldType: %v\n", f.Name, f.Type.Name())
    }

    
    // FieldName: Brand, FieldType: Brand
    // FieldName: FakeBrand, FieldType: Brand

go
}

總結:

  • FakeBrand是Brand的一個別名,在Vehicel中嵌入FakeBrand和Brand,Vehicel的型別會以名字的方式保留在Vehicle的成員中
  • FakeBrand和Brand 都有Show()方法, 呼叫時必須制定呼叫誰的, a.FakeBrand.Show()

九、條件陳述句

8.1 if陳述句

if 陳述句由布爾運算式后緊跟一個或多個陳述句組成,

語法

Go 編程語言中 if 陳述句的語法如下:

if 布爾運算式 {
   /* 在布爾運算式為 true 時執行 */
}

注意:if陳述句一定要加{}

8.2 if...else陳述句

if 陳述句 后可以使用可選的 else 陳述句, else 陳述句中的運算式在布爾運算式為 false 時執行,

語法

Go 編程語言中 if...else 陳述句的語法如下:

if 布爾運算式 {
   /* 在布爾運算式為 true 時執行 */
} else {
  /* 在布爾運算式為 false 時執行 */
}

注意:每一個 if else 都需要加入括號 同時 else 位置不能在新一行

尋找到 100 以內的所有的素數:

package main

import "fmt"
func main(){
    // var count,c int   //定義變數不使用也會報錯
    var count int
    var flag bool
    count=1
    //while(count<100) {    //go沒有while
    for count < 100 {
        count++
        flag = true;
        //注意tmp變數  :=
        for tmp:=2;tmp<count;tmp++ {
            if count%tmp==0{
                flag = false
            }
        }

        // 每一個 if else 都需要加入括號 同時 else 位置不能在新一行
        if flag == true {
            fmt.Println(count,"素數")
        }else{
            continue
        }
    }
}

8.3 switch case陳述句:

普通的switch陳述句:

var i = 0
switch i {
case 0:
case 1:
      fmt.Println(“1”)
case 2:
fmt.Println(“2”)
default:
     fmt.Println(“def”)
}

fallthrough陳述句:

var i = 0
switch i {
case 0:
        fallthrough
case 1:
      fmt.Println(“1”)
case 2:
fmt.Println(“2”)
default:
     fmt.Println(“def”)
}

注:加了fallthrough后,會直接運行【緊跟的后一個】case或default陳述句,不論條件是否滿足都會執行,后面的條件并不會再判斷了,

注意:Go 沒有三目運算子,所以不支持*?: 形式的條件判斷,

十、for回圈

for 回圈是一個回圈控制結構,可以執行指定次數的回圈,

語法

Go 語言的 For 回圈有 3 種形式,只有其中的一種使用分號,

和 C 語言的 for 一樣:

for init; condition; post { }

和 C 的 while 一樣:

for condition { }

和 C 的 for(;?? 一樣:

for { }
  • init: 一般為賦值運算式,給控制變數賦初值;
  • condition: 關系運算式或邏輯運算式,回圈控制條件;
  • post: 一般為賦值運算式,給控制變數增量或減量,

for陳述句執行程序如下:

  • 1、先對運算式 1 賦初值;
  • 2、判別賦值運算式 init 是否滿足給定條件,若其值為真,滿足回圈條件,則執行回圈體內陳述句,然后執行 post,進入第二次回圈,再判別 condition;否則判斷 condition 的值為假,不滿足條件,就終止for回圈,執行回圈體外陳述句,

for 回圈的 range 格式可以對 slice、map、陣列、字串等進行迭代回圈,格式如下:

for key, value := range oldMap {
    newMap[key] = value
}

實體

計算 1 到 10 的數字之和:

實體

package main

import "fmt"

func main() {
    sum := 0
    for i := 0; i <= 10; i++ {
        sum += i
    }
    fmt.Println(sum)
}

輸出結果為:

55

init 和 post 引數是可選的,我們可以直接省略它,類似 While 陳述句,

以下實體在 sum 小于 10 的時候計算 sum 自相加后的值:

實體

package main

import "fmt"

func main() {
    sum := 1
    for ; sum <= 10; {
        sum += sum
    }
    fmt.Println(sum)

    // 這樣寫也可以,更像 While 陳述句形式
    for sum <= 10{
        sum += sum
    }
    fmt.Println(sum)
}

輸出結果為:

16
16

無限回圈:

實體

package main

import "fmt"

func main() {
    sum := 0
    for {
      sum++ // 無限回圈下去
    }
    fmt.Println(sum) // 無法輸出
}

要停止無限回圈,可以在命令視窗按下ctrl-c

For-each range 回圈

這種格式的回圈可以對字串、陣列、切片等進行迭代輸出元素,

實體

package main
import "fmt"

func main() {
    strings := []string{"google", "runoob"}
    for i, s := range strings {
        fmt.Println(i, s)
    }


    numbers := [6]int{1, 2, 3, 5}
    for i,x:= range numbers {
        fmt.Printf("第 %d 位 x 的值 = %d\n", i,x)
    } 
}

以上實體運行輸出結果為:

0 google
1 runoob
第 0 位 x 的值 = 1
第 1 位 x 的值 = 2
第 2 位 x 的值 = 3
第 3 位 x 的值 = 5
第 4 位 x 的值 = 0
第 5 位 x 的值 = 0

十一、陣列

11.1 宣告陣列

Go 語言陣列宣告需要指定元素型別及元素個數,語法格式如下:

var variable_name [SIZE] variable_type

以上為一維陣列的定義方式,例如以下定義了陣列 balance 長度為 10 型別為 float32:

var balance [10] float32

11.2 初始化陣列

以下演示了陣列初始化:

var balance = [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}

初始化陣列中 {} 中的元素個數不能大于 [] 中的數字,

如果忽略 [] 中的數字不設定陣列大小,Go 語言會根據元素的個數來設定陣列的大小:

 var balance = [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}

該實體與上面的實體是一樣的,雖然沒有設定陣列的大小,

 balance[4] = 50.0

以上實體讀取了第五個元素,陣列元素可以通過索引(位置)來讀取(或者修改),索引從0開始,第一個元素索引為 0,第二個索引為 1,以此類推,

img


11.3 訪問陣列元素

陣列元素可以通過索引(位置)來讀取,格式為陣列名后加中括號,中括號中為索引的值,例如:

var salary float32 = balance[9]

11.4多維陣列

Go 語言支持多維陣列,以下為常用的多維陣列宣告方式:

var variable_name [SIZE1][SIZE2]...[SIZEN] variable_type

以下實體宣告了三維的整型陣列:

var threedim [5][10][4]int

11.5初始化多維陣列

多維陣列可通過大括號來初始值,以下實體為一個 3 行 4 列的二維陣列:

a = [3][4]int{  
 {0, 1, 2, 3} ,   /*  第一行索引為 0 */
 {4, 5, 6, 7} ,   /*  第二行索引為 1 */
 {8, 9, 10, 11},   /* 第三行索引為 2 */
}

注意:以上代碼中倒數第二行的 } 必須要有逗號,因為最后一行的 } 不能單獨一行,也可以寫成這樣:

a = [3][4]int{  
 {0, 1, 2, 3} ,   /*  第一行索引為 0 */
 {4, 5, 6, 7} ,   /*  第二行索引為 1 */
 {8, 9, 10, 11}}   /* 第三行索引為 2 */

11.6 訪問多維陣列

多維陣列通過指定坐標來訪問,如陣列中的行索引與列索引,例如:

val := a[2][3]
或
var value int = a[2][3]

以上實體訪問了二維陣列 val 第三行的第四個元素,

十二、函式

函式是基本的代碼塊,用于執行一個任務,

Go 語言最少有個 main() 函式,

你可以通過函式來劃分不同功能,邏輯上每個函式執行的是指定的任務,

函式宣告告訴了編譯器函式的名稱,回傳型別,和引數,

Go 語言標準庫提供了多種可動用的內置的函式,例如,len() 函式可以接受不同型別引數并回傳該型別的長度,如果我們傳入的是字串則回傳字串的長度,如果傳入的是陣列,則回傳陣列中包含的元素個數,


12.1 函式定義

Go 語言函式定義格式如下:

func function_name( [parameter list] ) [return_types] {
   函式體
}

函式定義決議:

  • func:函式由 func 開始宣告

  • function_name:函式名稱,函式名和引數串列一起構成了函式簽名,

  • parameter list:引數串列,引數就像一個占位符,當函式被呼叫時,你可以將值傳遞給引數,這個值被稱為實際引數,引數串列指定的是引數型別、順序、及引數個數,引數是可選的,也就是說函式也可以不包含引數,

  • return_types:回傳型別,函式回傳一列值,return_types 是該列值的資料型別,有些功能不需要回傳值,這種情況下 return_types 不是必須的,

  • 函式體:函式定義的代碼集合,

    注意:

    1.2.1 golang函式不支持多載,一個包不能有兩個函式名一樣的函式,

    1.2.2 函式也是一種型別,一個函式可以賦值給變數,

12.2 函式回傳多個值

Go 函式可以回傳多個值,例如:

package main

import "fmt"

func swap(x, y string) (string, string) {
   return y, x
}

func main() {
   a, b := swap("Google", "Runoob")
   fmt.Println(a, b)
}

以上實體執行結果為:

Runoob Google

12.3 函式return值的幾種情況

分三種情況

(以下 “指定回傳值”這句話, 僅指return后面直接跟著的回傳值)

1.退出執行,不指定回傳值
(1) 函式沒有回傳值

package main
import (
    "fmt"
)

func GetMoney(){
fmt.Println("money")
  return
}

func main(){
  GetMoney()
}

(2) 函式回傳值有變數名

package main
import (
    "fmt"
)

func GetMoney() (_amount int){
 _amount = 88
 fmt.Println("money: ",_amount)
  return
}

func main(){
  var amount  = GetMoney()
  fmt.Println("money: ",amount)
}

2.退出執行,指定回傳值

package main
import (
    "fmt"
)

func GetMoney() (_amount int){
 fmt.Println("money: ",_amount)
  return 88
}

func main(){
  var amount  = GetMoney()
  fmt.Println("money: ",amount)
}

運行結果:
money:  0
money:  88

3.退出執行,指定回傳值和指定默認值

package main
import (
    "fmt"
)

func GetMoney() (_amount int){
_amount  = 99     //如果return后面沒有指定回傳值,就用賦給“回傳值變數”的值
 fmt.Println("money: ",_amount)
  return 88       //如果return后面跟有回傳值,就使用return后面的回傳值
}

func main(){
  var amount  = GetMoney()  
  fmt.Println("money: ",amount)
}

運行結果:
money:  99
money:  88

12.4 可變引數

  • 0或多個引數
func add(arg…int) int {
}
  • 1或多個引數
func add(a int, arg…int) int {
}
  • 2或多個引數
func add(a int, b int, arg…int) int {
}

12.5 匿名函式

由一個不帶函式名的函式宣告和函式體組成,

func main()  {
    i := 1
 
    go func(i int) {
        time.Sleep(100*time.Millisecond)
        fmt.Println("i =", i)
    } (i)
 
    i++
    time.Sleep(1000*time.Millisecond)
}

十三、defer的用途

  • 當函式回傳時,先執行return,后執行defer陳述句,因此,可以用來做資源清理、關閉句柄、鎖的釋放、資料庫連接釋放,
  • 多個defer陳述句,按先進后出的方式執行,
  • defer陳述句中的變數,在defer宣告時就決定了,
 func a() {
          i := 0
          defer fmt.Println(i)
          i++
          return
} 

關閉檔案句柄:

func read() {
   file := open(filename)
   defer file.Close()
   //其他操作
}

鎖資源的釋放:

func read() {
   mc.Lock()
   defer mc.Unlock()
   //其他操作
}

資料庫連接釋放:

func read() {
   conn := openDatabase()
   defer conn.Close()
   //其他操作
}

十四、輸入的使用

1、輸入的使用

第一種寫法:fmt.Scanf("%d", &a)

第二種寫法:fmt.Scan(&a)

示例:

package main //必須有一個main包
 
import "fmt"
 
func main() {
    var a int //宣告變數
    fmt.Printf("請輸入變數a: ")
 
    //阻塞等待用戶的輸入
    //fmt.Scanf("%d", &a) //別忘了& 
    fmt.Scan(&a)
    fmt.Println("a = ", a)
}

#執行結果:

請輸入變數e:666
a =  666

  

Scanf %d只能接收整型,不能接收字符型

所以在輸入字符型變數時應該使用fmt.Scanf(“%c”,&a)

格式 含義
%% 一個%字面量
%b 一個二進制整數值(基數為2),或者是一個(高級的)用科學計數法表示的指數為2的浮點數
%c 字符型,可以把輸入的數字按照ASCII碼相應轉換為對應的字符
%d 一個十進制數值(基數為10)
%f 以標準記數法表示的浮點數或者復數值
%o 一個以八進制表示的數字(基數為8)
%p 以十六進制(基數為16)表示的一個值的地址,前綴為0x,字母使用小寫的a-f表示
%q 使用Go語法以及必須時使用轉義,以雙引號括起來的字串或者位元組切片[]byte,或者是以單引號括起來的數字
%s 字串,輸出字串中的字符直至字串中的空字符(字串以'\0‘結尾,這個'\0'即空字符)
%t 以true或者false輸出的布林值
%T 使用Go語法輸出的值的型別
%x 以十六進制表示的整型值(基數為十六),數字a-f使用小寫表示
%X 以十六進制表示的整型值(基數為十六),數字A-F使用小寫表示

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/229649.html

標籤:Go

上一篇:發現了一個關于 gin 1.3.0 框架的 bug

下一篇:Java中的HashTable詳解

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more