導包
示例:工程結構如下
GOPATH 設定為工程根目錄(E:\GO_PROJECT)
test1.go
:
package package_test
import "fmt"
/*
定義函式
*/
func Func1() {
fmt.Println("test1 func1")
}
test2.go
:
package package_test
import "fmt"
func Func2() {
fmt.Println("test2 func2")
}
testmain.go
:
package main
import "dir1/dir2" // 匯入的是【目錄名】
func main() {
package_test.Func1() // 呼叫的是【包名】
}
執行
:
// 方式一
go bulid testmain.go // 編譯(生成可執行檔案)
./ testmain // 執行(可執行檔案)
//方式二
go run testmain.go // 編譯+執行
導包結論
:
- 一個 GO 程式中一定要有 main 包和 main 函式,這是 GO 程式執行的入口。
- 編譯器會根據指定的相對路徑去搜尋包然後匯入,這個相對路徑是從 GOROOT 或 GOPATH(workspace)下的 src 下開始搜尋:
- GOROOT
- 專案 GOPATH
- 全域性 GOPATH
- GOlang 和 Java 的區別是,在 GO 中 import 的是目錄,而不是包名;並且 GO 沒有強制要求包名和目錄名需要一致,即包和目錄是兩個不同的概念。
import 匯入的是原始檔所在的目錄名,而不是定義的包名
。在程式碼中引用包內的成員時,使用定義的包名而不是目錄名
。- 在習慣上將包名和目錄名保證一致,但這並不是強制規定。
在同一級目錄中,所有原始檔只能使用相同的包名
。同一包名下的變數名、函式名等不能重複
。- 多個目錄下的相同包名,彼此無關。
- 包中的成員(如函式)以名稱⾸字母⼤⼩寫,決定其訪問許可權:
- ⾸字母⼤寫,可被包外訪問(即
public
) - ⾸字母⼩寫,僅包內成員可以訪問(即
private
)
- ⾸字母⼤寫,可被包外訪問(即
工程管理
為了更好的管理專案中的檔案,GO 要求將檔案都要放在相應的目錄中,具體規定了以下目錄:
src 目錄
:以程式碼包的形式組織並儲存 GO 原始碼檔案(比如 .go 檔案、.c 檔案、.h 檔案、.s 檔案等)pkg 目錄
:用於存放經由 go install 命令構建安裝後的程式碼包(包含 GO 庫原始碼檔案)的 ".a" 歸檔檔案。bin 目錄
:與 pkg 目錄類似,在透過 go install 命令完成安裝後,儲存由 GO 命令原始碼檔案生成的可執行檔案。
以上目錄稱為工作區(Workspace),工作區其實就是一個對應於特定工程的目錄。
src 目錄用於包含所有的原始碼,是 GO 命令列工具一個強制的規則;而 pkg 和 bin 則無需手動建立,GO 命令列工具在構建過程中會自動建立這些目錄。
變數
變數宣告
- 變數名必須以字母或下劃線開頭,後面可以跟任意數量的字母、數字或下劃線。
- 變數的三種宣告&初始化方式如下:
// 方式一:先宣告(有預設值),後賦值
var a int
a = 10
var b, c string
b = "b"
c = "c"
// 方式二:宣告+賦值
var a int = 10
// 方式三:自動推導型別(注意:這種方式只能在函式內使用)
a := 10
b, c := 11, 12
b, c = c, b // 值交換
// 匿名變數
_, i, _, j = 1, 2, 3, 4
格式化輸出
格式 | 含義 |
---|---|
%% | 一個 % 字面量 |
%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 使用小寫表示 |
基本資料型別
型別 | 名稱 | 長度 | 零值(預設值) | 說明 |
---|---|---|---|---|
bool | 布林型別 | 1 | false | 其值不為真即為假,不可以用數字代表 true 或 false |
byte | 位元組型 | 1 | 0 | uint8 的別名 |
int, uint | 整型 | - | 0 | 有符號 32 位或無符號 64 位 |
int8 | 整型 | 1 | 0 | -128 ~ 127 |
uint8 | 整型 | 1 | 0 | 0 ~ 255 |
int16 | 整型 | 2 | 0 | -32768 ~ 32767 |
uint16 | 整型 | 2 | 0 | 0 ~ 65535 |
int32 | 整型 | 4 | 0 | -2147483648 到 2147483647 |
rune | 整型 | 4 | 0 | int32 的別名 |
uint32 | 整型 | 4 | 0 | 0 到 4294967295(42 億) |
int64 | 整型 | 8 | 0 | -9223372036854775808到 92233720368547758070 |
uint64 | 整型 | 8 | 0 | 到 18446744073709551615(1844 京) |
float32 | 浮點型 | 4 | 0.0 | 小數位精確到 7 位 |
float64 | 浮點型 | 8 | 0.0 | 小數位精確到 15 位 |
string | 字串 | - | "" | utf-8 字串 |
值的字面量(literal)是指程式碼中值的文字表示。一個值可能存在多種字面量表示。
表示基本型別值的文字稱為基本字面量。基本字面量也被稱為字面量常量或未命名常量。
示例:
func main() {
var ch byte = 97 // 宣告字元型別
fmt.Printf("ch=%c", ch) // 輸出 a
}
這裡定義了 ch 是一個字元型別,賦值卻是一個整數 97,列印的結果是小寫字元 'a'。
原因是:計算機不能直接儲存字元型別,只能轉成數字儲存,那為什麼小寫字元 'a' 對應的整數是 97 呢?因為計算機是根據 ASCII 碼來儲存的。
強制型別轉換
package main
import "fmt"
func main() {
chinese := 97
math := 99
english := 96
fmt.Printf("總分是 %d,平均分(整數)是 %d, 平均分(浮點數)是 %f", chinese+math+english, (chinese+math+english)/3, float32(chinese+math+english)/3)
// 總分是 292,平均分(整數)是 97, 平均分(浮點數)是 97.333336
}
注意:
GO 語言中不允許隱式轉換,所有型別轉換必須顯式宣告(強制轉換),而且轉換隻能發生在兩種相互相容的型別之間
var ch byte = 'c'
// var a int = ch // cannot use ch (type byte) as type int in assignment
var b int = int(ch) // 資料型別名(待轉換的值)
- int 轉 float 強制轉換:多小數
- float 轉 int 強制轉換:丟精度
接收鍵盤輸入
package main
import fmt
func main() {
var age int
fmt.Println("請輸入年齡:")
fmt.Scanf("%d", &age) // 將接收的值賦給age變數
fmt.Printf("age=%d", age)
}
常量
常量宣告
func main() {
// 變數:程式執行期間,值可以改變,宣告使用var
// 常量:程式執行期間,值不可以改變,宣告使用const
const a int = 11
// a = 12 // error:常量不可以修改
const b = 12 // 自動推導型別(常量不可以使用:=)
}
iota 列舉
iota 常量生成器:用於生成一組遞增的整型常量,減去了每行都要寫一遍初始化表示式的繁瑣。
func main() {
// 在一個 const 宣告語句中,在第一個宣告的常量所在的行,iota 將會被置為 0,然後在每一個有常量宣告的行遞增 1
const (
a = iota // 0
b = iota // 1
c = iota // 2
)
const d = iota // iota 遇到const,會被置為 0
// 可以只寫一個iota
const (
e = iota // 0
f // 1
g
)
// 同一行值一樣
const (
h = iota // 0
i, j, k = iota, iota, iota // 1
l = iota // 2
)
}
運算子
算術運算子
運算子 | 術語 |
---|---|
+ | 加(既可以完成兩個數字相加,又可以連線兩個字串) |
- | 減 |
* | 乘 |
/ | 除 |
% | 取模(取餘) |
++ | 後自增(GO 語言中沒有前自增) |
-- | 後自減(GO 語言中沒有前自減) |
賦值運算子
運算子 | 說明 | 示例 |
---|---|---|
= | 普通賦值 | c = a + b 將 a + b 表示式結果賦值給 c |
+= | 相加後再賦值 | c += a 等價於 c = c + a |
-= | 相減後再賦值 | c -= a 等價於 c = c - a |
*= | 相乘後再賦值 | c *= a 等價於 c = c * a |
/= | 相除後再賦值 | c /= a 等價於 c = c / a |
%= | 求餘後再賦值 | c %= a 等價於 c = c % a |
關係運算子
運算子 | 術語 | 示例 | 結果 |
---|---|---|---|
== | 相等於 | 4 == 3 | false |
!= | 不等於 | 4 != 3 | true |
< | 小於 | 4 < 3 | false |
> | 大於 | 4 > 3 | true |
<= | 小於等於 | 4 <= 3 | false |
>= | 大於等於 | 4 >= 1 | true |
邏輯運算子
運算子 | 術語 | 示例 | 結果 |
---|---|---|---|
! | 非 | !a | 如果 a 為假,則 !a 為真 如果 a 為真,則 !a 為假 |
&& | 與 | a && b | 如果 a 和 b 都為真,則結果為真,否則為假 |
|| | 或 | a || b | 如果 a 和 b 有一個為真,則結果為真,二者都為假時,結果為假 |
條件語句
if 語句
package main
import "fmt"
func main() {
var score int
fmt.Println("請輸入成績:")
fmt.Scanf("%d", &score)
// if 語句支援一個初始化語句+判斷條件
if good_score := 90; score >= good_score {
fmt.Println("優秀")
} else if soso_score := 60; score >= soso_score {
fmt.Println("及格")
} else {
fmt.Println("需要努力")
}
}
switch 語句
注意:case 後面預設自帶 break
,如果想執行完成某個 case 後繼續執行後面的 case,可以使用 fallthrough 關鍵字。
func main() {
bonus := 5000
var score string
finish := true
fmt.Println("請輸入年終評級:")
switch fmt.Scanf("%s", &score); score {
case "A":
bonus += 1000
case "B":
bonus += 5000
case "C":
case "D":
bonus -= 1000
default:
finish = false
fmt.Println("輸入有誤,請輸入正確的評級")
}
if finish {
fmt.Printf("您的年終獎為:%d", bonus)
}
}
迴圈語句
GO 中的 3 種迴圈方式:
package main
import "fmt"
func main() {
// 第1種方式
for i := 0; i < 5; i++ {
fmt.Printf("i=%d\n", i)
}
// 第2種方式:類似while迴圈
num := 1
for num < 5 {
fmt.Printf("num=%d\n", num)
num++
}
// 第3種方式
for {
fmt.Println("死迴圈")
}
}
函式
函式定義
函式就是將一堆程式碼進行封裝,以便重用的一種機制。
func 函式名(){
函式體
}
引數列表
import "fmt"
func MyFunc(a int, b int, args ...int) { // 注意:不定長引數必須放在引數列表最後
fmt.Println("定長引數為:", a, b)
for i, data := range args { // 若不需要編號,則可使用 for _, data := range args
fmt.Println("編號為:", i)
fmt.Println("資料為:", data)
}
}
func main() {
MyFunc(1, 2, 3, 4, 5)
}
列印結果:
定長引數為: 1 2
編號為: 0
資料為: 3
編號為: 1
資料為: 4
編號為: 2
資料為: 5
函式返回值
// 定義返回值型別
func Add(a int, b int) int {
return a + b
}
// 給返回值命名
func Sub(a int, b int) (sub int) {
sub = a - b
return sub // 也可以只寫 return
}
// 返回多個值
func Sub(a, b, c int) {
a, b, c = 1, 2, 3
return a, b, c // 也可以只寫 return
}
函式型別
在 GO 語言中還有另外一種定義使用函式的方式,就是函式型別。
所謂的函式型別,就是將函式作為一種型別可以用來定義變數
,基本使用如下:
func Test(a, b int) int {
return a + b
}
// 定義函式型別:即需要傳遞兩個整型引數,有一個整型返回值的函式型別
type FuncType func(a int, b int) int // type關鍵字後面跟著型別的名字(FunType)
func main() {
var result FuncType // 型別是FuncType型別,即函式型別
result = Test
s := result(1, 2)
fmt.Printf("s=%d", s)
}
匿名函式
所謂匿名函式就是沒有名字的函式。匿名函式最主要的功能就是實現了閉包
。
package main
import "fmt"
func main() {
num := 9
// 使用方式一:定義匿名函式並賦值給變數
f1 := func() {
// 在匿名函式中可以直接訪問main()中定義的區域性變數
// 並且在匿名函式中對變數的值進行了修改,最終會影響到整個main()函式中定義的變數值
num++
fmt.Println("匿名函式:", num)
}
f1() // 10
fmt.Println("main函式:", num) // 10
// 使用方式二:透過函式型別
type FuncType func() // 沒有引數,沒有返回值
var f2 FuncType = f1
f2()
// 使用方式三:直接呼叫
func(a, b int) {
fmt.Println("a + b =", a+b)
}(3, 6)
// 有參有返回值的匿名函式
min, max := func(a, b int) (min, max int) {
if a > b {
return b, a
} else {
return a, b
}
}(12, 13)
fmt.Printf("min=%d, max=%d\n", min, max)
}
遞迴函式
示例:階乘
func Test(num int) int {
if num == 1 {
return 1
}
return num * Test(num-1)
}
func main() {
var num int
fmt.Println("請輸入需要階乘的數:")
fmt.Scanf("%d", &num)
fmt.Printf("%d的階乘結果為:%d", num, Test(num))
}