Go語言基礎語法總結

行人觀學發表於2020-07-24

1. 認識HelloWorld

在前面的《Go的安裝和使用》這篇文章中已經寫過HelloWorld.go了,現在就來逐行認識一下它。

package main

import "fmt"

func main() {
	fmt.Println("Hello, World!")
}

第1行:我們的Go程式是由——package構成的,包的宣告形式為:package <包名>。該行的意思是:當前HelloWorld.go檔案屬於main包。

第2行:如果你使用過Java或Python,那你對import肯定不陌生。該行的意思是:匯入一個名為fmt的包。如果需要匯入多個包,有兩種寫法:

import "fmt"
import "math"

或者使用分組形式同時匯入多個包

import (
	"fmt"
	"math/rand"
)

顯然第二種使用括號,以分組的形式匯入更加方便。

第3行:我們使用func關鍵字來宣告一個函式,在這個例子中,我們宣告的是main函式。如果你有過其他語言的程式設計經驗,肯定熟悉main函式的作用:程式的入口。

第4行:我們的函式內容放在函式的{}中,在該例中,呼叫了fmt包中的列印方法,由此可見,Golang語言呼叫函式的方法和Java、Python一樣使用小數點:<包名>.<函式名>

看完這幾行簡單程式碼,我們發現:在Go語言中,並不需要分號;來結束語句。

2. 變數

2.1. 命名規範

Go語言要求識別符號以一個字母或下劃線開頭,後面可以跟任意數量的字母、數字、下劃線。不能使用關鍵字作為識別符號

區分大小寫,變數num和變數Num是兩個變數。

2.2. 宣告

Go語言的變數宣告方式和其他語言(如C、Java、Python)不太一樣。

比如,我們宣告一個為int型別的變數a:

var a int

可以看出,Go語言宣告變數的方式為:

var <變數名> <變數型別>

我們還可以同時宣告多個型別相同的變數:

var <變數名1>, <變數名2>, <變數名3>, ... <變數型別>

使用分組同時宣告多個型別不同的變數:

var (
	<變數名1> <變數型別1>
    <變數名2> <變數型別2>
    <變數名3> <變數型別3>
    ...
)

下面是一個具體的例子:

package main

import "fmt"

var i bool//型別在後

func main() {
	var a, b, c int//多個
	fmt.Println(i, a, b, c)
}

2.3. 初始化

當我們宣告變數時,可以對其進行初始化。當有初始值時,我們可以省略變數的型別,變數會根據對應的初始值獲取正確的型別。見下例:

package main

import "fmt"

var a int = 1
var b, c bool = true, false

func main() {
	var i, j, k = 2, false, "行小觀"	//省略變數型別
	fmt.Println(a, b, c, i, j, k)
    k = "行小觀2號" //賦值操作
    fmt.Println(k)
}

若我們在宣告變數時,不給變數賦初始值,則這些變數會被賦予“零值”。

這些零值為:

  1. 數值型別為0
  2. 布林型別為false
  3. 字串為""(即空字串)

如果上例中的程式碼中的變數不進行初始化,即:

package main

import "fmt"

var a int
var b, c bool

func main() {
	var i int
	var j bool
	var k string
	fmt.Println(a, b, c, i, j, k)
}

則列印結果為:0 false false 0 false (變數k由於是空字串,所以列印出來了但是看不到)

2.4. 短變數宣告

前面介紹了當我們宣告變數時有以下幾種方式:

var i int	//宣告一個int變數
var i, j, k int	//同時宣告多個int變數
var i int = 1	//宣告一個int變數並初始化
var i, j, k int = 1, 2, 3	//同時宣告多個int變數並分別初始化
var i = 1	//省略型別,宣告一個int變數並初始化
var i, j, k = 1, 2, 3	//省略型別,同時宣告多個int變數並分別初始化

除此之外還有一種更簡潔的宣告變數的方式:

i := 1
i, j, k := 1, 2, 3

當我們使用:=宣告變數時,不用寫var也不用寫型別,但是這種方式只能在函式內部使用,不能在函式外部使用。當在函式外部宣告變數時,只能使用var

package main

import "fmt"

var a int = 1	//函式外部

func main() {
	var i, j int = 2, 3	//函式內部

	k := 4 //只能在函式中使用
	v1, v2, v3 := true, false, "行小觀"

	fmt.Println(a, i, j, k, v1, v2, v3)
}

此外,我們宣告的變數必須要使用,如果宣告瞭變數,但是沒使用,會在編譯時報錯。比如:

package main

import "fmt"

func main() {
	var i bool
	var j int //宣告瞭,但沒使用,會報錯
	fmt.Println(i)
}

對於我們匯入的包也有此要求,即:匯入的包必須使用。

3. 資料型別

3.1. 布林型別

布林型別為bool,值可取truefalse,預設值為false

3.2. 字串型別

字串型別為string,預設為空字串""

3.3. 數值型別

整數型別分為:

  • 有符號數:intint8int16int32 (rune)int64

  • 無符號數:uintuint8 (byte)uint16uint32uint64

其中intuint的兩種型別的長度相同,取決於具體的編譯器,比如在32位系統上通常為32位,在64位系統上通常為64位。

int8uint8這些型別則是Go語言直接定義好位數的型別。runebyteint32uint8的別名。

當我們需要使用整數時,應當儘量使用int型別。當然,如果你有特殊的理由使用其他整數型別,便另當他論。

浮點數型別有兩種:float32float64,注意沒有所謂的float型別。

複數型別也有兩種:complex64complex128

注意:不同型別的變數之間不能直接進行賦值或其他運算(比如加減乘除)

package main

import "fmt"

var (
	a int = 1
	b int8 = 2
	c int16
)

func main() {
	c = b	//不同型別之間進行賦值操作,[報錯1]
	d := a + b	//不同型別之間進行相加運算,[報錯2]
	fmt.Printf("c = %v, d = %v", c, d)
}

以上程式碼在編譯過程中會報錯:

[報錯1]:cannot use b (type int8) as type int16 in assignment

[報錯2]:invalid operation: a + b (mismatched types int and int8)

3.4. 型別轉換

前面一節說過:不同型別的變數之間不能直接進行賦值或其他運算,所以我們可以間接地做。

比如:將int8型別轉換為int型別,這樣就可以間接地進行賦值和其他運算。

使用表示式T(v)將變數v的值的型別轉換為T。注意是轉換的是變數的值,變數本身的型別不變。

package main

import "fmt"

var (
	a int = 1
	b int8 = 2
	c uint
	d int64
)

func main() {
	c = uint(b) //將變數b的值2的型別從int8轉換為uint
	d = int64(a) + int64(b)

	fmt.Printf("c(%T):%v = b(%T):%v\n", c, c, b, b)
	fmt.Printf("a(%T):%v + b(%T):%v = d(%T):%v\n", a, a, b, b, d, d)
}

注意:Go語言中的型別轉換是顯示的,表示式T()是必須的,不能省略。

4. 常量

常量是固定的值,值在程式執行期間不會改變。

常量可以定義為數值、字串、布林型別

常量的宣告方式和變數差不多,區別在於常量需要用const關鍵字修飾,不能使用:=進行宣告。

package main

import "fmt"

const num int = 555
var a int = 1

func main() {

	const world = "世界"
	const truth = true

	fmt.Println("Hello,", world)
	fmt.Println("num = ", num)
	fmt.Println("a = ", a)
	fmt.Println("對嗎?", truth)
}

5. 初識函式

如果你之前學習過C或者Java等語言,肯定已經對函式(方法)有了一定的認識。

簡單地來說,函式是對能完成某個功能的部分程式碼的抽象。當以後再需要該功能,我們只需要呼叫其對用的函式即可,不必再重複編寫程式碼。

5.1. 函式的宣告

我們在前面的內容已經使用到了函式,即main()。我們使用func關鍵字宣告函式。

func func_name() {
    
}

5.2. 函式的引數

Go語言中,函式可以有0個或多個引數。

package main

import "fmt"

func printName(name string, age int) {
	fmt.Println("我叫", name, ", 今年", age, "歲了")
}

func sayHello() {
	fmt.Println("行小觀說:“你好”")
}

func main() {
	printName("行小觀", 1)
	sayHello()
}

如果你有多個引數的型別相同,你可以進行簡寫,只需要在這幾個相同的引數最後寫一遍型別即可。

func foo(x int, y int, z int)
可以簡寫為:
func foo(x, y, x int)

5.3. 函式的型別和返回值

函式的型別在函式名之後。(儘快適應Go的這種風格:型別在變數名後)

package main

import "fmt"

func add(x int, y int) int {
	return x + y
}

func main() {
	fmt.Println(add(1, 2))
}

當函式沒有返回值時,不需要寫函式的型別:

func sayHello() {//沒有返回值,不寫函式型別
	fmt.Println("行小觀說:“你好”")
}

函式可以有0個或多個返回值

多個返回值就意味著該函式有多個返回值型別:

package main

import "fmt"

func sumAndDiff(x, y int) (int, int) { //兩個返回值型別
	sum := x + y
	diff := x - y
	return sum, diff //兩個返回值
}

func main() {
	a, b := sumAndDiff(5, 1)
	fmt.Println(a, b)
}

注意:和引數不同,有幾個返回值就寫幾個返回值型別,不能簡寫。

Go語言還提供了另一種函式返回的方式:命名返回值。

顧名思義,我們通過給返回值進行命名,使用空return語句,這樣會直接返回已命名的返回值。如上例的sumAndDiff函式可以寫為:

func sumAndDiff(x, y int) (sum int, diff int) {//提前命名返回值
	sum = x + y
	diff = x - y //返回值在函式中被初始化
	return //返回值已經初始化了,不需要再在return語句中寫變數了
}

下面總結一下函式的使用:

func functionName(input1, input11 type1, input2 type2 ...) (type1, type11, type2 ...){
    
    //函式體
    
    return value1, value11, value2 ...
}

或者命名返回值

func functionName(input1, input11 type1, input2 type2 ...) (output1 type1, output11 type11, output2 type2 ...){
    
    //函式體
    output1 = ...
    output11 = ...
    output2 =  ...
    ...
    return
}

6. 匯出名

前面我們已經使用了import匯入功能,比如improt "fmt",該行程式碼可以讓我們在本包中使用其他包裡的函式。

那麼我們如何讓其他包能夠使用到本包的方法或變數呢?答案是:將方法或變數匯出

在Go語言中,如果一個名字以大寫字母開頭,那麼它就是已匯出的,這意味著別的包可以使用它。(相當於Java中的public的作用)

比如我們常用的列印函式fmt.Println(...),可以看到Println()的首字母是大寫的,所以我們能夠匯入fmt包後使用該方法。

7. 流程控制語句

7.1. if語句

if語句是條件判斷語句,用來判斷是否滿足某種條件,如果滿足,則執行某段程式碼;如果不滿足,則不執行。

if ... {
    //程式碼
} else if ... {
    //程式碼
} else {
    //程式碼
}

注意格式:條件判斷語句不需要使用小括號()

下面是幾個例子:

if a > 0 {//如果滿足a>0,則列印Hello, World
    fmt.Println("Hello, World")
}
if a > 0 {//如果滿足a>0,則列印 Hello, World
    fmt.Println("Hello, World!")
} else {//否則(即不滿足a>0),則列印 你好,世界!
    fmt.Println("你好,世界!")
}
if a > 5 {//如果滿足a>5,則列印 Hello, World
    fmt.Println("Hello, World!")
} else if a <= 5 && a > 0 {//如果滿足0<a<=5,則列印 好好學習,天天向上
    fmt.Println("好好學習,天天向上")
} else {//否則(即上面的條件都不滿足),則列印 你好,世界!
    fmt.Println("你好,世界!")
}

Go語言的if語句有一個特性:可以在條件表示式前執行一個簡單的語句。下面是一個例子:

package main

import "fmt"

func sum(x, y int) int {
	return x + y
}

func main ()  {
	if i := sum(1, 2); i > 0 {
		fmt.Println("Hello, World!")//作用域內,能列印i
	}
    //fmt.Println(i)//作用域外,不能列印i
}

在if語句中,使用sum(x, y int)函式計算出i的值,再進行判斷。注意:變數i的作用域只在if語句中有效。

7.2. for語句

for語句是Go語言中的迴圈控制語句。它有幾種形式:

(一)基本形式:

for 初始化語句; 條件表示式; 後置語句 {
    //迴圈體程式碼
}
  • 初始化語句:在第一次迴圈前之前,且只執行這一次
  • 條件表示式:每次迴圈都會計算該表示式,如果滿足(值為true)則繼續迴圈;如果不滿足(值為false),則跳出迴圈
  • 後置語句:每次迴圈執行完都會執行該語句

下面是一個例子,迴圈列印5次"Hello,World!"

for i := 0; i < 5; i++ {
    fmt.Println("Hello, World!", i)
}

注意該例的初始化語句i := 0是一個短變數宣告,變數i只在該for迴圈中生效。

(二)省略形式:

for迴圈中的初始化語句和後置語句是可以省略的。

i := 0
for ; i < 5; i++ {
    fmt.Println("Hello, World!", i)
}
i := 0
for ; i < 5; {
    fmt.Println("Hello, World!", i)
    i++
}

從某種意義上來講,上面兩個例子並沒有省略初始化語句或後置語句,只是改變了位置。

(三)while形式

諸如C、Java等語言中都有while迴圈,但是Go語言中沒有while迴圈,但是我們可以使用for迴圈來實現“while迴圈”。

其實(二)省略形式中的第二個for迴圈例子就已經可以看做是while迴圈了。我們再稍做改進:

i := 0
for i < 5 {//去掉兩個分號,只寫條件表示式
    fmt.Println("Hello, World!", i)
    i++
}

(四)無限迴圈形式

//列印無限多個Hello, World!
for  {
    fmt.Println("Hello, World!")
}

7.3. break和continue

上面提到的迴圈語句只有當條件表示式的值為false時,才會停止迴圈。但實際開發中,我們可能在條件表示式的值為true的情況下,需要退出迴圈。這種時候,就需要使用breakcontinue語句。

break語句用來跳出當前迴圈,continue語句用來跳過本次迴圈。

下面是兩個例項(改進上面迴圈列印5次"Hello,World!"的例子):

例項1:增加需求,當列印完第2遍Hello,World!時,停止列印

for i := 0; i < 5; i++ {
    if i == 2 {
        break
    }
    fmt.Println("Hello, World!", i)
}

例項2:增加需求,不列印第3遍Hello,World!

for i := 0; i < 5; i++ {
    if i == 2 {
        continue
    }
    fmt.Println("Hello, World!", i)
}

7.4. switch語句

我們可以使用if...else if...else if...進行一連串的條件判斷,但是這樣過於繁雜。switch語句就是用來簡化這個問題的。

switch 變數 {
    case 選項1 :
    	//操作1程式碼
    case 選項2 :
    	//操作2程式碼
    case 選項3 :
    	//操作3程式碼
    case 選項n:
    	//操作n程式碼
	default :
    	//預設操作
}

switch語句中有許多case和一個default,只有當變數和case的選項相匹配時,才會執行對應的操作程式碼。如果沒有case的選項可以匹配,則預設執行default的程式碼。

下面是一個例子:

package main

import "fmt"

func sum(x, y int) int {
	return x + y
}

func main ()  {
    result := sum(3, 2)
	switch result {
	case 1 :
		fmt.Println("結果為1")
	case 2, 3, 4:	//多種情況聚合在一起
		fmt.Println("結果為2或3或4")
	case sum(1, 4):	//支援表示式
		fmt.Println("結果為5")
	default:
		fmt.Println("其他結果")
	}
}

從上面的例子可以看出,Go語言中switch的case支援常量(不必為整數)、表示式、多個值聚合。注意:不論是常量、表示式,還是多個值聚合,都要保證常量、表示式的值、多個值的型別和switch的變數相同。

switch語句的匹配順序是自上到下。Go語言自動為每個case提供了break語句,所以在眾多選項中只能執行1個casedefault,然後結束,剩餘的不再執行。

但是可以使用fallthrough強制執行剩餘的case:

result := sum(1, 1)
switch result {
    case 1 :
    fmt.Println("結果為1")
    fallthrough
    case 2, 3, 4:
    fmt.Println("結果為2或3或4")
    fallthrough
    case sum(1, 4):
    fmt.Println("結果為5")
    fallthrough
    default:
    fmt.Println("其他結果")
}

還是上面的那個例子,但是在每個case中使用fallthrough,現在的列印結果為:

結果為2或3或4
結果為5
其他結果

如有錯誤,還請指正。

文章首發於公眾號『行人觀學』

在這裡插入圖片描述

相關文章