Go語言基礎語法總結
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)
}
若我們在聲明變數時,不給變數賦初始值,則這些變數會被賦予「零值」。
這些零值為:
- 數值類型為0
- 布爾類型為false
- 字元串為””(即空字元串)
如果上例中的程式碼中的變數不進行初始化,即:
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
,值可取true
或false
,默認值為false
。
3.2. 字元串類型
字元串類型為string
,默認為空字元串""
。
3.3. 數值類型
整數類型分為:
-
有符號數:
int
、int8
、int16
、int32 (rune)
、int64
-
無符號數:
uint
、uint8 (byte)
、uint16
、uint32
、uint64
、
其中int
和uint
的兩種類型的長度相同,取決於具體的編譯器,比如在32位系統上通常為32位,在64位系統上通常為64位。
像int8
、uint8
這些類型則是Go語言直接定義好位數的類型。rune
、byte
是int32
、uint8
的別名。
當我們需要使用整數時,應當盡量使用
int
類型。當然,如果你有特殊的理由使用其他整數類型,便另當他論。
浮點數類型有兩種:float32
和float64
,注意沒有所謂的float
類型。
複數類型也有兩種:complex64
和complex128
注意:不同類型的變數之間不能直接進行賦值或其他運算(比如加減乘除)
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的情況下,需要退出循環。這種時候,就需要使用break
或continue
語句。
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個case
或default
,然後結束,剩餘的不再執行。
但是可以使用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
其他結果
如有錯誤,還請指正。
文章首發於公眾號『行人觀學』