Go 語言基礎之變量和常量#
變量和常量是編程中必不可少的部分,也是很好理解的一部分。
標識符與關鍵字#
標識符#
在編程語言中標識符就是程序員定義的具有特殊意義的詞,比如變量名,常量名,函數名等等。Go 語言中標識符由字母數字和_(下劃線) 組成,並且只能以字母和_開頭。
關鍵字#
關鍵字是指編程語言中預先定義好的具有特殊含義的標識符。關鍵字和保留字都不建議用作變量名。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 語言中還有 37 個保留字。
Constants: true false iota nil
Types: int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
float32 float64 complex128 complex64
bool byte rune string error
Functions: make len cap new append copy close delete
complex real imag
panici recover
變量#
變量的來歷#
程序運行過程中的數據都是保存在內存中,我們想要在代碼中操作某個數據時就需要去內存上找到這個變量,但是如果我們直接在代碼中通過內存地址去操作變量的話,代碼的可讀性會非常差而且還容易出錯,所以我們就利用變量將這個數據的內存地址保存起來,以後直接通過這個變量就能找到內存上對應的數據了。
變量的類型#
變量的功能是存儲數據。不同的變量保存的數據類型可能會不一樣。Go 語言中的每一個變量都有自己的類型,並且變量必須經過生命才能開始使用。
變量的聲明#
Go 語言中的變量需要聲明後才能使用,同一作用域內不支持重複聲明。並且 Go 語言的變量聲明後必須使用。
標準聲明#
Go 語言的變量聲明格式為:
var 變量名 變量類型
變量聲明以關鍵字 var
開頭,變量類型放在變量的後面,行尾無需分號。
類型推導#
有時候我們會將變量的類型省略,這個時候編譯器會根據等號右邊的值來推導變量的類型完成初始化。
var name = "Q1mi"
var ag=18
短變量聲明#
在函數內部,可以使用更簡略的 :=
方式聲明並初始化變量
package main
import (
"fmt"
)
//全局變量m
var m = 10
func main() {
n := 10
m := 200 // 此處聲明局部變量m
fmt.Println(m, n)
}
匿名變量#
在使用多重賦值時,如果想要忽略某個值,可以使用匿名變量。匿名變量使用一個下劃線_表示.
func foo()(int, string) {
return 10, "Q1mi"
}
func main() {
x, _ := foo()
_, y := foo()
fmt.Println("x=", x)
fmt.PrintLn("y=", y)
}
匿名變量不占用命名空間,不會分配內存,所以匿名變量之間不存在重複聲明。注意事項:
-
函數外的每個語句都必須以關鍵字開始 (var, const, func 等)
-
:= 不能使用在函數外
-
_
多用於占位,表示忽略值。常量#
相對於變量,常量是恆定不變的值,多用於定義程序運行期間不會改變的那些值。常量的聲明和變量聲明非常類似,只是把
var
換成了const
, 常量在定義的時候必須賦值。並且在整個程序運行期間,他們的值都不能再發生變化了。iota#
iota
是 Go 語言的常數計數器,只能在常量的表達式中使用。iota
在 const 關鍵字出現時將被重置為 0。Const 中每新增一行常量聲明將使iota
計數一次(iota
可理解為 const 語句塊中的行索引)。使用 iota 能簡化定義,在定義枚舉時很有用。邏輯運算符#
運算符 | 描述 |
---|---|
&& | 邏輯 AND 運算符。如果兩邊的操作數都是 True,則為 True |
|| | 邏輯 OR 運算符 |
! | 邏輯非運算符 |
變量的作用域#
全局變量,大寫字母開頭,所有地方都可以訪問,跨 package 訪問是需要帶 package 名稱。全局變量,小寫字母開頭,本 package 內都可以訪問。局部變量,僅本函數內可以訪問。內部聲明的變量可以跟外部聲明的變量有衝突,以內部的為準。僅 {} 圈定的作用域內可以訪問,可以跟外部的變量有衝突。
變量的生命週期#
變量的生命週期指的是在程序運行期間閉麥昂有效存在的時間間隔。對於在包級聲明的變量來說,它們的生命週期和整個程序的運行周期是一致的。而相比之下,在局部變量的聲明周期則是動態的:從每次創建一個新變量的聲明語句開始,知道該變量不再被引用為止,然後變量的存儲空間可能會被回收。注意:
- 由於一個變量的有效周期只取決於是否可達,因此一個循環迭代內部的局部變量的生命週期可能超出其局部作用域。同時,局部變量可能在函數返回之後依然存在。
Go 語言基礎之基本數據類型與註釋#
Go 語言中有豐富的數據類型,除了基本的整型,浮點型,布爾型,字符串之外,還有數組,切片,結構體,函數,map,通道等。Go 語言的基本類型和其他語言大同小異。
基本數據類型#
整型#
整型分為以下兩大類:按長度分為:int 8, int 16, int 32, int 64 對應的無符號整型:uint 8, uint 16, uint 32, uint 64 其中,uint 8 就是我們熟知的 byte 類型。int 16 對應的 c 語言中的 short 型,Int 64 對應 c 語言中的 long 型。
類型 | 描述 |
---|---|
uint 8 | 無符號 8 位整型 (0-255) |
uint 16 | 無符號 16 位整型 (0-65535) |
uint 32 | 無符號 32 位整型 (0-4294967295) |
uint 64 | 無符號 64 位 (0-18446744073709551615) |
int 8 | 有符號 8 位整型 (-128-127) |
int 16 | 有符號 16 位整數 (-32768 到 32767)) |
int 32 | 有符號 32 位整數 () |
布爾值#
Go 語言中以 bol
類型進行聲明布爾型數據,布爾型數據只有 true
和 false
兩個值 注意:
- 布爾類型變量的默認值為 false.
- Go 語言中不允許被整型強制轉換為布爾型
- 布爾型無法參與數值運算,也無法與其他類型進行轉換。
字符串#
Go 語言中的字符串以原生數據類型出現,使用字符串就像使用其他原生數據類型 (int, bool, float 32, float 64 等) 一樣。Go 語言裡的字符串的內部實現使用 UTF-8
編碼。字符串的值為 雙引號(")
中的內容,可以在 Go 語言的與阿馬忠直接添加非 ASII 碼字符,例如:
s1 := "hello"
字符串的常用操作#
方法 | 介紹 |
---|---|
len (str) | 求長度 |
+ 或 fmt.Sprintf | 拼接字符串 |
strings. Split | 分割 |
strings. Contains | 判斷是否包含 |
strings. HasPrefix ,strings. HasSuffix | 前綴 / 後綴判斷 |
strings. Index (), strings. LastIndex() | 子串出現的位置 |
strings. Join (a[] string, sep String) | join 操作 |
Byte 和 rune 類型#
組成每個字符串的元素叫做 “字符”,可以通過遍歷或者單個獲取字符串元素獲得字符。字符用單引號(‘)包裹起來,如:
var a := '中'
var b ::= 'x'
// byte 等價與uint8 ,rune 等價於 int32
// rune 可以表示任意的unicode字符
複合數據類型#
類型 | 默認值 | 說明 |
---|---|---|
array | 值類型 | |
struct | 值類型 | |
string | "" | UTF-8 字符串 |
slice | nil | 引用類型 |
map | nil | 引用類型 |
channel | nil | 引用類型 |
interface | nil | 接口 |
function | nil | 函數 |
數組#
數組是一塊連續的內存空間,在聲明的時候必須指定長度,且長度不能改變。所以數組在聲明的時候就可以把內存空間分配好,並賦上默認值,即可完成初始化。
初始化數組#
var arr [5]int = [5]{} // 數組必須指定長度和類型,且長度和類型指定後不可改變
var arr2 = [5]int{}
var arr3 = [5]int{3,2} // 給前兩個元素賦值
var arr4 = [5]int{2:15, 4:30} // 指定index複製
var arr5 = [...]int{3,2,6,5,4} // 根據{}裡元素的個數推斷出數組的長度
var arr6 = [...]struct{
name string
age int
}{{"Tome", 18}, {"Jim", 20}} // 數組的元素類型是由匿名結構體給定
訪問數組裡的元素#
-
通過 index 訪問
- 首元素 arr [0]
- 末元素 arr [len (arr)-1]
-
訪問二維數組裡的元素
-
位於第三行第 4 列的元素 arr [2][3]
// 遍歷數組裡的元素 for i, ele := range arr { fmt.Printf("index=%d, element=%d\n", i, ele) } // 遍歷二維數組 for row, array := range arr { for col, ele := range array { fmt.Printf("arr[%d][%d]=%d\n", row, col, ele) } }
cap 和 len#
-
-
Cap 代表 capacity 容量
-
Len 代表 length 長度
-
len 代表目前數組裡幾個元素,cap 代表給數組分配的內存空間可以容納多少個元素
-
由於數組初始化後不會改變,不需要給它預留內存空間,所以 len (arr) = cap (arr)
數組傳參#
-
數組的長度和類型都是數組類型的一部分,函數傳遞數組類型時,這兩部分都必須吻合
-
Go 語言沒有按引用傳參,全部都是按值傳參,即傳遞數組實際上上傳的是數組的拷貝,當數組的長度很大時,僅傳參開銷都很大。
-
如果想修改函數外部的數組,就把它的指針(數組在內存裡的地址)傳進來。
切片#
type slice struct {
array unsafe.Pointer
len int
cap int
}
初始化切片#
var s []int // 切片聲明,len=cap=0
s = []int{} //初始化, len=cap=0
s = make([]int, 3) //初始化, len=cap=3
s = make([]int, 3, 5) //初始化,len=3, cap=5
s = []int{11,2,3,4,5} //初始化, le=cap=5
s2d := [][]int {
{1}, {2,3}, // 二維數組各行列數相等,但二維切片各行的len可以不相等
}
Append#
-
切片相對於數組最大的特點就是可以追加元素,可以自動擴容
-
追加的元素放到預留的內存空間裡,同時 len 加 1
-
如果預留的空間已用完,則會重新申請一塊更大的內存空間,capacity 變成之前的 2 倍 (cap<1024) 或 1.25 倍 (cap>1024)。把原內困空間數據拷貝過來,在新內存空間上執行 append 操作
截取子切片#
-
s := make([]int, 3,5)
// len=3, cap=5 -
sub_slice = s[1:3]
// len = 2, cap=4 -
剛開始,子切片和母切片共享底層的內存空間,修改子切片會反映到母切片上,在子切片上執行 append 會把新元素放到母切片預留的內存空間上
-
當子切片不斷執行 append,耗完了母切片預留的內存空間,子切片跟目前片就會發生內存分離,伺候兩個切片沒有任何關係。
切片傳參#
-
Go 語言參數傳參,傳的都是值,即傳切片會把切片的
{arrayPointer, len, cap}
這 3 個字段拷貝一份 -
由於傳的是底層數組的指針,所以可以直接修改底層數組裡的元素。
map#
Map 是一個無序的 key/value 對的集合,其中所有的 key 都是不同的,然後通過給定的 key 可以在常數時間複雜度內檢索,更新或刪除對應的 value。
指針#
定義#
自定義類型和字符串#
-
類性別名
- Type byte = uint 8
- Type rune = int 32
-
自定義類型
-
type signal uint 8
-
Type ms map[string]string
-
Type add func (a, b int) int
-
Type user struct {name string; age int}
強制類型轉換#
var i int = 9 var by byte = byte(i) // int轉為byte i = int(by) // byte轉為Int fmt.Printf("i=%d\n", i)
-
-
Byte 和 int 可以相互轉換
-
Float 和 int 可以相互轉換,小數位丟失
-
Bool 和 int 不能相互轉換
-
不同長度的 int 或 float 可以相互轉換
-
String 可以轉換為 [] byte 或 [] rune 類型,byte 或 rune 可以轉換為 string
-
低精度向高精度轉換沒問題,高精度向低精度轉換會丟失位數
-
無符號向有符號轉換,最高位是符號位
註釋#
-
單行註釋。以 // 大頭
-
多行註釋。連續多行以 // 大頭,或者在段前使用 /*, 段尾使用 */
-
多行註釋之間不能出現空行
-
NOTE: 因人注意,TODO:將來需要優化,Deprecated: 變量或函數強烈建議不要在使用
-
註釋行前加縮頸即可寫 Go 代碼
-
包註釋。在 package xxx 的上方。一個包只需要在一個地方寫包註釋,通常會專門寫一個 doc. Go,裡面只有一行 package xxx 和關於包的註釋
-
結構體註釋。在 type xxx struct 上方
-
函數註釋。在 func xxx () 上方
-
行註釋。在行上方或右側
Godoc#
-
Go doc 是 Go 自帶的命令
-
Go doc entrance class/util
-
Go doc 可以為項目代碼導出網頁版的註釋文檔
-
需要先安裝 go get golang. Org/x/tools/cmd/godoc
-
啟動 http: godoc -http:6060
包和工具#
包#
包簡介#
-
所有導入的包必須在每個文件的開頭顯式聲明。
-
禁止包的環状以來,因為沒有循環依賴,包的依賴關係形成一個有向無環圖,每個包可以被獨立編譯,並且很有可能被並發編譯
-
編譯後包的目標文件不僅僅記錄包本生的導出信息,目標文件同時還記錄了包的依賴關係
導入路徑#
每個包是由一個全局唯一的字符串所標識的導入路徑定位。出現在 import 語句中的導入路徑也是字符串。Go 語言的規範並沒有指明包的導入路徑字符串的具體含義,導入路徑的具體含義是由構建工具來解釋的。
包聲明#
在每個 Go 語言源文件的開頭都必須有包聲明語句。包聲明語句的主要目的是確定當前包被其它包導入時默認的標識符(也成為包名)。
包和命名#
-
儘量避免包名使用可能被經常用於局部變量的名字,這樣可能導致用戶重命名導入包。
-
包名一般採用單數的形式。標準庫的 bytes, errors 和 strings 使用了複數形式,這是為了避免和預定義的類型衝突,同樣還有 go/types 是為了避免和 type 關鍵字衝突
工具#
Go 語言的工具箱集合了一系列的功能的命令集。它可以看作是一個包管理器,用於包的查詢,計算包的依賴關係,從遠程版本控制系統和下載它們等任務。他也是一個構建系統,計算文件的以來關係,然後調用編譯器,匯編器和連接器構建程序,也是一个單元測試和基準測試的驅動程序。
下載包#
使用 Go 語言工具箱的 go 命令,不僅可以根據包導入路徑找到本地工作區的包,甚至可以從互聯網上找到和更新包。
使用命令 go get
可以下載一個單一的包或者用 ...
下載整個子目錄裡面的每個包。Go 語言工具箱的 go 命令同時計算並下載所依賴的每個包。
一旦 go get
命令下載了包,然後就是安裝包或包對應的可執行的程序。
構建包#
go build
命令編譯命令行參數指定的每個包。如果包是一個庫,則忽略輸出結果;這可以用於檢測包的是否可以正確編譯。如果包的名字是 mian, go build
將調用連接器在當前目錄創建一個可執行程序;以導入路徑的最後一段作為可執行程序的名字。
因為每個目錄只包含一個包,因此每個對應可執行程序或者叫 unix 术语中的包,會要求放到一個獨立的目錄中。
接口#
接口類型是對其他類型行為的抽象和概括;因為接口類型不會和特定的實現細節綁定在一起,通過這種抽象的方式,可以讓我們的函數更加靈活和更具有適應能力。Go 語言中接口類型的獨特之處在於它是滿足隱式實現的。也就是說,我們沒有必要對於給定的具體類型定義所有滿足已經存在的具體類型確不會去改變這些類型的定義。