GO語言基礎(結構+語法+型別+變數)
Go語言結構
Go語言語法
Go語言型別
Go語言變數
Go 語言結構
Go 語言的基礎組成有以下幾個部分:
- 包宣告
- 引入包
- 函式
- 變數
- 語句 & 表示式
- 註釋
接下來讓我們來看下簡單的程式碼,該程式碼輸出了"Hello World!":
例項
package main import "fmt" func main() { /* 這是我的第一個簡單的程式 */ fmt.Println("Hello, World!") }
讓我們來看下以上程式的各個部分:
- 第一行程式碼 package main 定義了包名。你必須在原始檔中非註釋的第一行指明這個檔案屬於哪個包,如:package main。package main表示一個可獨立執行的程式,每個 Go 應用程式都包含一個名為 main 的包。
- 下一行 import "fmt" 告訴 Go 編譯器這個程式需要使用 fmt 包(的函式,或其他元素),fmt 包實現了格式化 IO(輸入/輸出)的函式。
- 下一行 func main() 是程式開始執行的函式。main 函式是每一個可執行程式所必須包含的,一般來說都是在啟動後第一個執行的函式(如果有 init() 函式則會先執行該函式)。
- 下一行 /*...*/ 是註釋,在程式執行時將被忽略。單行註釋是最常見的註釋形式,你可以在任何地方使用以 // 開頭的單行註釋。多行註釋也叫塊註釋,均已以 /* 開頭,並以 */ 結尾,且不可以巢狀使用,多行註釋一般用於包的文件描述或註釋成塊的程式碼片段。
- 下一行 fmt.Println(...) 可以將字串輸出到控制檯,並在最後自動增加換行字元 \n。
使用 fmt.Print("hello, world\n") 可以得到相同的結果。
Print 和 Println 這兩個函式也支援使用變數,如:fmt.Println(arr)。如果沒有特別指定,它們會以預設的列印格式將變數 arr 輸出到控制檯。
- 當識別符號(包括常量、變數、型別、函式名、結構欄位等等)以一個大寫字母開頭,如:Group1,那麼使用這種形式的識別符號的物件就可以被外部包的程式碼所使用(客戶端程式需要先匯入這個包),這被稱為匯出(像面嚮物件語言中的 public);識別符號如果以小寫字母開頭,則對包外是不可見的,但是他們在整個包的內部是可見並且可用的(像面嚮物件語言中的 protected )。
執行 Go 程式
讓我們來看下如何編寫 Go 程式碼並執行它。步驟如下:
- 開啟編輯器如Sublime2,將以上程式碼新增到編輯器中。
- 將以上程式碼儲存為 hello.go
- 開啟命令列,並進入程式檔案儲存的目錄中。
- 輸入命令 go run hello.go 並按回車執行程式碼。
- 如果操作正確你將在螢幕上看到 "Hello World!" 字樣的輸出。
$ go run hello.go Hello, World!
- 我們還可以使用 go build 命令來生成二進位制檔案:
$ go build hello.go $ ls hello hello.go $ ./hello Hello, World!
注意
需要注意的是 { 不能單獨放在一行,所以以下程式碼在執行時會產生錯誤:
例項
package main import "fmt" func main() { // 錯誤,{ 不能在單獨的行上 fmt.Println("Hello, World!") }
Go 語言基礎語法
上一章節我們已經瞭解了 Go 語言的基本組成結構,本章節我們將學習 Go 語言的基礎語法。
Go 標記
Go 程式可以由多個標記組成,可以是關鍵字,識別符號,常量,字串,符號。如以下 GO 語句由 6 個標記組成:
fmt.Println("Hello, World!")
6 個標記是(每行一個):
1. fmt 2. . 3. Println 4. ( 5. "Hello, World!" 6. )
行分隔符
在 Go 程式中,一行代表一個語句結束。每個語句不需要像 C 家族中的其它語言一樣以分號 ; 結尾,因為這些工作都將由 Go 編譯器自動完成。
如果你打算將多個語句寫在同一行,它們則必須使用 ; 人為區分,但在實際開發中我們並不鼓勵這種做法。
以下為兩個語句:
fmt.Println("Hello, World!") fmt.Println("yp:runoob.com")
註釋
註釋不會被編譯,每一個包應該有相關注釋。
單行註釋是最常見的註釋形式,你可以在任何地方使用以 // 開頭的單行註釋。多行註釋也叫塊註釋,均已以 /* 開頭,並以 */ 結尾。如:
// 單行註釋 /* Author by yp 我是多行註釋 */
識別符號
識別符號用來命名變數、型別等程式實體。一個識別符號實際上就是一個或是多個字母(A~Z和a~z)數字(0~9)、下劃線_組成的序列,但是第一個字元必須是字母或下劃線而不能是數字。
以下是有效的識別符號:
mahesh kumar abc move_name a_123 myname50 _temp j a23b9 retVal
以下是無效的識別符號:
- 1ab(以數字開頭)
- case(Go 語言的關鍵字)
- a+b(運算子是不允許的)
字串連線
Go 語言的字串可以通過 + 實現:
例項
package main import "fmt" func main() { fmt.Println("Google" + "Runoob") }
以上例項輸出結果為:
GoogleRunoob
關鍵字
下面列舉了 Go 程式碼中會使用到的 25 個關鍵字或保留字:
break
|
default
|
func
|
interface
|
select
|
case
|
defer
|
go
|
map
|
struct
|
chan
|
else
|
goto
|
package
|
switch
|
const
|
fallthrough
|
if
|
range
|
type
|
continue
|
for
|
import
|
return
|
var
|
除了以上介紹的這些關鍵字,Go 語言還有 36 個預定義識別符號:
append
|
bool
|
byte
|
cap
|
close
|
complex
|
complex64
|
complex128
|
uint16
|
copy
|
false
|
float32
|
float64
|
imag
|
int
|
int8
|
int16
|
uint32
|
int32
|
int64
|
iota
|
len
|
make
|
new
|
nil
|
panic
|
uint64
|
print
|
println
|
real
|
recover
|
string
|
true
|
uint
|
uint8
|
uintptr
|
程式一般由關鍵字、常量、變數、運算子、型別和函式組成。
程式中可能會使用到這些分隔符:括號 (),中括號 [] 和大括號 {}。
程式中可能會使用到這些標點符號:.、,、;、: 和 …。
Go 語言的空格
Go 語言中變數的宣告必須使用空格隔開,如:
var age int;
語句中適當使用空格能讓程式更易閱讀。
無空格:
fruit=apples+oranges;
在變數與運算子間加入空格,程式看起來更加美觀,如:
fruit = apples + oranges;
格式化字串
Go 語言中使用 fmt.Sprintf 格式化字串並賦值給新串:
例項
package main import ( "fmt" ) func main() { // %d 表示整型數字,%s 表示字串 var stockcode=123 var enddate="2020-12-31" var url="Code=%d&endDate=%s" var target_url=fmt.Sprintf(url,stockcode,enddate) fmt.Println(target_url) }
輸出結果為:
Code=123&endDate=2020-12-31
Go 語言數據型別
在 Go 程式語言中,資料型別用於宣告函式和變數。
資料型別的出現是為了把資料分成所需記憶體大小不同的資料,程式設計的時候需要用大資料的時候才需要申請大記憶體,就可以充分利用記憶體。
Go 語言按類別有以下幾種資料型別:
序號
|
型別和描述
|
1
|
布林型
布林型的值只可以是常量 true 或者 false。一個簡單的例子:var b bool = true。
|
2
|
數字型別
整型 int 和浮點型 float32、float64,Go 語言支援整型和浮點型數字,並且支援複數,其中位的運算採用補碼。
|
3
|
字串型別:
字串就是一串固定長度的字元連線起來的字元序列。Go 的字串是由單個位元組連線起來的。Go 語言的字串的位元組使用 UTF-8 編碼標識 Unicode 文字。
|
4
|
派生型別:
包括:
(a) 指標型別(Pointer)
(b) 陣列型別
(c) 結構化型別(struct)
(d) Channel 型別
(e) 函式型別
(f) 切片型別
(g) 介面型別(interface)
(h) Map 型別
|
數字型別
Go 也有基於架構的型別,例如:int、uint 和 uintptr。
序號
|
型別和描述
|
1
|
uint8
無符號 8 位整型 (0 到 255)
|
2
|
uint16
無符號 16 位整型 (0 到 65535)
|
3
|
uint32
無符號 32 位整型 (0 到 4294967295)
|
4
|
uint64
無符號 64 位整型 (0 到 18446744073709551615)
|
5
|
int8
有符號 8 位整型 (-128 到 127)
|
6
|
int16
有符號 16 位整型 (-32768 到 32767)
|
7
|
int32
有符號 32 位整型 (-2147483648 到 2147483647)
|
8
|
int64
有符號 64 位整型 (-9223372036854775808 到 9223372036854775807)
|
浮點型
序號
|
型別和描述
|
1
|
float32
IEEE-754 32位浮點型數
|
2
|
float64
IEEE-754 64位浮點型數
|
3
|
complex64
32 位實數和虛數
|
4
|
complex128
64 位實數和虛數
|
其他數字型別
以下列出了其他更多的數字型別:
序號
|
型別和描述
|
1
|
byte
類似 uint8
|
2
|
rune
類似 int32
|
3
|
uint
32 或 64 位
|
4
|
int
與 uint 一樣大小
|
5
|
uintptr
無符號整型,用於存放一個指標
|
Go 語言變數
變數來源於數學,是計算機語言中能儲存計算結果或能表示值抽象概念。
變數可以通過變數名訪問。
Go 語言變數名由字母、數字、下劃線組成,其中首個字元不能為數字。
宣告變數的一般形式是使用 var 關鍵字:
var identifier type
可以一次宣告多個變數:
var identifier1, identifier2 type
例項
package main import "fmt" func main() { var a string = "Runoob" fmt.Println(a) var b, c int = 1, 2 fmt.Println(b, c) }
以上例項輸出結果為:
Runoob 1 2
變數宣告
第一種,指定變數型別,如果沒有初始化,則變數預設為零值。
var v_name v_type v_name = value
零值就是變數沒有做初始化時系統預設設定的值。
例項
package main import "fmt" func main() { // 宣告一個變數並初始化 var a = "RUNOOB" fmt.Println(a) // 沒有初始化就為零值 var b int fmt.Println(b) // bool 零值為 false var c bool fmt.Println(c) }
以上例項執行結果為:
RUNOOB 0 false
- 數值型別(包括complex64/128)為 0
- 布林型別為 false
- 字串為 ""(空字串)
- 以下幾種型別為 nil:
var a *int var a []int var a map[string] int var a chan int var a func(string) int var a error // error 是介面
例項
package main import "fmt" func main() { var i int var f float64 var b bool var s string fmt.Printf("%v %v %v %q\n", i, f, b, s) }
輸出結果是:
0 0 false ""
第二種,根據值自行判定變數型別。
var v_name = value
例項
package main import "fmt" func main() { var d = true fmt.Println(d) }
輸出結果是:
true
第三種,如果變數已經使用 var 宣告過了,再使用 := 宣告變數,就產生編譯錯誤,格式:
v_name := value
例如:
var intVal int intVal :=1 // 這時候會產生編譯錯誤,因為 intVal 已經宣告,不需要重新宣告
直接使用下面的語句即可:
intVal := 1 // 此時不會產生編譯錯誤,因為有宣告新的變數,因為 := 是一個宣告語句
intVal := 1 相等於:
var intVal int intVal =1
可以將 var f string = "Runoob" 簡寫為 f := "Runoob":
例項
package main import "fmt" func main() { f := "Runoob" // var f string = "Runoob" fmt.Println(f) }
輸出結果是:
Runoob
多變數宣告
//型別相同多個變數, 非全域性變數 var vname1, vname2, vname3 type vname1, vname2, vname3 = v1, v2, v3 var vname1, vname2, vname3 = v1, v2, v3 // 和 python 很像,不需要顯示宣告型別,自動推斷 vname1, vname2, vname3 := v1, v2, v3 // 出現在 := 左側的變數不應該是已經被宣告過的,否則會導致編譯錯誤 // 這種因式分解關鍵字的寫法一般用於宣告全域性變數 var ( vname1 v_type1 vname2 v_type2 )
例項
package main var x, y int var ( // 這種因式分解關鍵字的寫法一般用於宣告全域性變數 a int b bool ) var c, d int = 1, 2 var e, f = 123, "hello"
//這種不帶宣告格式的只能在函式體中出現
//g, h := 123, "hello"
func main(){ g, h := 123, "hello" println(x, y, a, b, c, d, e, f, g, h) }
以上例項執行結果為:
0 0 0 false 1 2 123 hello 123 hello
值型別和引用型別
所有像 int、float、bool 和 string 這些基本型別都屬於值型別,使用這些型別的變數直接指向存在記憶體中的值:
當使用等號 = 將一個變數的值賦值給另一個變數時,如:j = i,實際上是在記憶體中將 i 的值進行了拷貝:
你可以通過 &i 來獲取變數 i 的記憶體地址,例如:0xf840000040(每次的地址都可能不一樣)。
值型別變數的值儲存在堆中。
記憶體地址會根據機器的不同而有所不同,甚至相同的程式在不同的機器上執行後也會有不同的記憶體地址。因為每臺機器可能有不同的儲存器佈局,並且位置分配也可能不同。
更復雜的資料通常會需要使用多個字,這些資料一般使用引用型別儲存。
一個引用型別的變數 r1 儲存的是 r1 的值所在的記憶體地址(數字),或記憶體地址中第一個字所在的位置。
這個記憶體地址稱之為指標,這個指標實際上也被存在另外的某一個值中。
同一個引用型別的指標指向的多個字可以是在連續的記憶體地址中(記憶體佈局是連續的),這也是計算效率最高的一種儲存形式;也可以將這些字分散存放在記憶體中,每個字都指示了下一個字所在的記憶體地址。
當使用賦值語句 r2 = r1 時,只有引用(地址)被複制。
如果 r1 的值被改變了,那麼這個值的所有引用都會指向被修改後的內容,在這個例子中,r2 也會受到影響。
簡短形式,使用 := 賦值操作符
我們知道可以在變數的初始化時省略變數的型別而由系統自動推斷,宣告語句寫上 var 關鍵字其實是顯得有些多餘了,因此我們可以將它們簡寫為 a := 50 或 b := false。
a 和 b 的型別(int 和 bool)將由編譯器自動推斷。
這是使用變數的首選形式,但是它只能被用在函式體內,而不可以用於全域性變數的宣告與賦值。使用操作符 := 可以高效地建立一個新的變數,稱之為初始化宣告。
注意事項
如果在相同的程式碼塊中,我們不可以再次對於相同名稱的變數使用初始化宣告,例如:a := 20 就是不被允許的,編譯器會提示錯誤 no new variables on left side of :=,但是 a = 20 是可以的,因為這是給相同的變數賦予一個新的值。
如果你在定義變數 a 之前使用它,則會得到編譯錯誤 undefined: a。
如果你宣告瞭一個區域性變數卻沒有在相同的程式碼塊中使用它,同樣會得到編譯錯誤,例如下面這個例子當中的變數 a:
例項
package main import "fmt" func main() { var a string = "abc" fmt.Println("hello, world") }
嘗試編譯這段程式碼將得到錯誤 a declared but not used。
此外,單純地給 a 賦值也是不夠的,這個值必須被使用,所以使用
fmt.Println("hello, world", a)
會移除錯誤。
但是全域性變數是允許宣告但不使用的。 同一型別的多個變數可以宣告在同一行,如:
var a, b, c int
多變數可以在同一行進行賦值,如:
var a, b int var c string a, b, c = 5, 7, "abc"
上面這行假設了變數 a,b 和 c 都已經被宣告,否則的話應該這樣使用:
a, b, c := 5, 7, "abc"
右邊的這些值以相同的順序賦值給左邊的變數,所以 a 的值是 5, b 的值是 7,c 的值是 "abc"。
這被稱為 並行 或 同時 賦值。
如果你想要交換兩個變數的值,則可以簡單地使用 a, b = b, a,兩個變數的型別必須是相同。
空白識別符號 _ 也被用於拋棄值,如值 5 在:_, b = 5, 7 中被拋棄。
_ 實際上是一個只寫變數,你不能得到它的值。這樣做是因為 Go 語言中你必須使用所有被宣告的變數,但有時你並不需要使用從一個函式得到的所有返回值。
並行賦值也被用於當一個函式返回多個返回值時,比如這裡的 val 和錯誤 err 是通過呼叫 Func1 函式同時得到:val, err = Func1(var1)。