Golang基礎知識
- 1初識Golang
- 1.1 Go的語法要求
- 1.1.1 token
- 1.2 變數和常量
- 變數
- 常量
- 1.3 基本資料型別
- 1.3.1 布爾型別
- 1.3.2 整型
- 1.3.3 浮點型
- 1.3.4 復數型別
- 1.3.5 字串
- 1.3.6 rune 型別
- 1.4 復合資料型別
- 1.4.1 指標
- 1.4.2 陣列
- 1.4.3 切片
- 1.4.4 map
- 1.4.5 struct
- 1.5 控制陳述句
- 1.5.1 if 陳述句
- 1.5.2 switch 陳述句
- 1.5.3 for陳述句
1初識Golang
首先讓我們問候一下世界
package main
import "fmt"
func main(){
fmt.Println("hello, world!\n")
}
運行結果:

對世界進行了一波問候后,我們對這個簡單的程式進行解讀分析
(1)第一行我們定義了一個名為 package 的包,main是可執行程式的包名,所有的Go源程式檔案頭部必須有一個包宣告陳述句,
(2)然后用 關鍵字 import 匯入了一個“fmt”檔案,fmt 是 format 的縮寫,是一個標準的包,有點類似C語言中的頭檔案,
(3)關鍵字 func 宣告定義了一個函式, 函式名為main, 在Go語言中 main 代表一個程式的入口,沒有 main 函式的程式就像與一間沒有沒有門的密室,在C語言中也是如此,
(4)main函式里面呼叫了 fmt 包里的 Println 函式, 其中 “Hello, world!” 是一個字串常量,而 \n 是一個轉義字符, 換行,
1.1 Go的語法要求
1.1.1 token
token 是構成源程式的基本不可再分割的單元,編譯器的編譯源程式的第一步就是將源程式分割為一個個獨立的 token,這就是語法分析的程序,而token 又可以分為關鍵字,識別符號,運算子、分隔符和字面常量等,
如圖所示:

其中運算子本身就是一個天然的分隔符,同時其自身也是一個 token,
純分隔符本身布局有任何語法意義,只是作為其他 token 的分割功能,例如空格、字符表、換行符等,
從1.2中的代碼可以分析出
關鍵字:
package import func
識別符號:
main fmt Println
字面量:
“fmt” “hello, world!\n”
運算子:
( ) { } .
一個Golang源程式的基本就是由各種 token 構成,
基本框架:
package main
import (
"...."
)
func main(){
....
}
1.2 變數和常量
變數
變數的理解很簡單,如其名,可以變化的量,其官方定義是指沒有固定的值,可以改變的數, 通常在寫程式時都需要用到各中資料,而變數能夠方便程式員對記憶體資料進行訪問,
1.變數的完整宣告
var varName dataType = value
var a int = 10
var a int = 3 * 5
var a int = b
1.短型別宣告
varName := value
//注意:
//:= 宣告只能出現在函式內
//Go編譯器會自動進行資料型別推斷
變數名的命名規則
開頭字符必須是字母或下劃線,后面可跟多個字符,數字或下劃線,
常量
常量”的廣義概念是:‘不變化的量’(例如:在計算機程式運行時,不會被程式修改的量,在Golang中常量使用一個名稱來系結一塊記憶體地址,且該記憶體地址里面存放的內容不可改變,
//類似列舉
package main
import "fmt"
const(
c0 = 2 << iota
c1
c2
c3
c4
)
func main(){
fmt.Println(c0, c1, c2, c3, c4)//結果:2 4 8 16 32
}
還有字串常量,例如:
a := "hello world"
1.3 基本資料型別
Golang 內置有七類基本資料型別
布爾型別:bool
整形:byte int int8 int32 int64 uint uint8 uint16 uint32 uint64 uintptr
浮點型:float32 float64
復數:complex64 complex128
字符:rune
字串:string
1.3.1 布爾型別
布爾型別只有兩個值,true 和 false,true 代表真,false 代表假,是Go內置的兩個預宣告識別符號,
var a bool
a = true
//or
a := false
//布爾型別資料和整數不能進行相互轉換
var a bool
a = 1 //error 1是整型字面量
//邏輯比較判斷運算式的回傳值都是布爾型別資料
//條件成立的回傳值為true,否則回傳false
x := 2
y := 1
var b bool = x > y //b = true
var b bool = (x < 0) && (y > 0) // b = false
//if 和 for 陳述句中的條件部分的回傳值也是bool型別
if a < b{
print(b)
}else{
printf(a)
}
for ; true; {//相當于C中的while(1)
}
//宣告的bool型別變數如果沒有進行初始化,則默認值為false
var c bool // c is false
1.3.2 整型
整型變數的定義方式:
//標準型
var a int
a = 1
//縮減型
a := 1
Go中內置了12種整型型別,byte int int6 int8 int32 int64 uint uint8 uint16 uint32 uint64 uintptr,每種型別可容納的資料大小不同,并且不同整型相互賦值需要強制轉換
var a int = 1
var b int32 = 2
b = a //error
b = (int32)a //true
1.3.3 浮點型
浮點型變數用于表示存盤包含小數的資料空間,Go中有 float32 和 float64 兩種,
注意事項:
(1)浮點數字面量被默認為 float64 型別
var f = 1.00
(2)在學習C語言時,用浮點型資料進行計算經常出現小數點后面的資料有一些細微的差別,這是與精度丟失有關,計算機很難進行浮點數的精確表示和存盤,因此兩個浮點數之間不能進行 == 或 != 判斷操作,
1.3.4 復數型別
Golang 內置的復數形式有兩種,分別是 complex64 和 complex128,復數在計算機中使用兩個浮點數表示,一個表示實部,一個表示虛部,complex64 是兩個 float32 構成的,complex128 是由兩個float64 構成的,表示方法和數學表示法一樣,
var value1 complex64 = 3.1 + 5.1
value2 := 3.1 + 6i
//Go 有三個內置函式處理復數
var v = complex(2.1, 3) //構造一個復數
a := real(v) //回傳復數實部
b := image(v) //回傳復數虛部
1.3.5 字串
基本介紹:
字串就是一串固定長度的字符連接起來的字符序列, Go 的字串是由單個位元組連接起來的,Golang 的字串位元組使用 UTF-8 編碼標識 Unicode 文本,
案例演示:
package main
import "fmt"
func main(){
//演示 Golang 中 string 的基本使用
var address string = "上海黃浦區 1203"
fmt.Println(address)
}
運行結果:

字串型別:string
注意事項和使用細節:
1.Golang 的字串位元組使用 UTF-8 編碼標識 Unicode 文本, 這樣 golang 統一使用 UTF-8 編碼,中文亂碼問題就不會困擾程式員了,
2.字串一旦賦值就不可更改了,在 Go 中字串是不可變的,
//字串一旦賦值就不可更改了,在 Go 中字串是不可變的
var str = "hello"
str[0] = 'a'
運行:

3.字串的表示形式
(1)雙引號,會識別轉義字符
(2)反引號,以字串的原生形態輸出,包括換行和特殊字符,可以實作防止輸出、攻擊源代碼等效果,
str2 := "abc\nadd"
fmt.Println(str2)
結果:

反引 ``
str := `
package main
import "fmt"
func main(){
//演示 Golang 中 string 的基本使用
// var address string = "上海黃浦區 1203"
// fmt.Println(address)
//字串一旦賦值就不可更改了,在 Go 中字串是不可變的
// var str = "hello"
// str[0] = 'a'
// str2 := "abc\nadd"
// fmt.Println(str2)
}`
fmt.Println(str)
運行:

字串的拼接方式
str := "hello" + "world"
str += "hehe"
運行:

4.基本資料轉換string
方式一:使用 fmt包的方法
案例說明:
package main
import (
"fmt"
)
func main(){
//使用第一中方式來轉換 fmt.Sprintf方法
var num1 int = 99
var str string //void str
str = fmt.Sprintf("%d", num1)
fmt.Printf("str type is %T, str = %q\n", str, str)
//輸出:str type is string, str = "99"
var num2 float64 = 120.345
str = fmt.Sprintf("%f", num2)
fmt.Printf("str type is %T, str = %q\n", str, str)
//輸出:str type is string, str = "120.345000"
var b bool = true
str = fmt.Sprintf("%t", b)
fmt.Printf("str type is %T, str = %q\n", str, str)
//輸出:str type is string, str = "true"
}
方式二:使用 strconv 包的方法
案例說明
//第二種方式:使用 strconv 函式
//func FormatInt(i int64, base int) string
//回傳i的base進制的字串表示,base 必須在2到36之間,
//結果中會使用小寫字母'a'到'z'表示大于10的數字,
var num3 int = 16
var str string
str = strconv.FormatInt(int64(num3), 10)
fmt.Printf("str type is %T, str = %q\n", str, str)
//輸出:str type is string, str = "16"
1.3.6 rune 型別
GO 內置的兩種字符型別:一種是 uint8 型別,或者叫 byte 型,代表了 ASCII 碼的一個字符,另一種
是 rune 型別,代表一個 UTF-8 字符,當需要處理中文、日文或者其他復合字符時,則需要用到 rune 型別,
rune 型別等價于 int32 型別,
1.4 復合資料型別
1.4.1 指標
對于指標的理解,我個人認為并沒有太大的困難,在學習C的時候,自從我學了指標和動態內存分配后,就再也沒用過陣列,并且還覺得用陣列很lwo(如有冒犯,請保住 -_- 狗頭),我個人認為指標是真的好用,非常的naice,指標的理解也很簡單,指標和其他型別一樣,也是一種變數,他是它是用來存盤地址的變數,不同型別的地址對應不同型別的指標
圖解:

Golang 中指標的特點:
(1)結構體指標訪問結構體欄位使用 “ . ” 點運算子, 沒有“->”運算子
例如:
package main
import "fmt"
type persion struct{
name string
age int
}
func main(){
//方式1
//方式2
p2 := persion{"張山", 20}
fmt.Println(p2)
//方式3-&
var p3 *persion = new(persion)
//因為p3是指標,因此標準的給給欄位賦值方式
(*p3).name = "李四" //也可以寫成p3.name = "李四"
(*p3).age = 8439 //原因:go的設計者為了程式員方便,底層會對 p3.name = "李四" 進行處理
fmt.Println(*p3) //會給 p3 加上取值運算子 (*p3).name = "李四"
//方式4-{}
var p4 *persion = &persion{}//也可以 var p4 *persion = &persion{"suyue", 23}
//因為 p4 是一個指標,因此標準的訪問欄位的方法
//(*p4).name = ...
//也可以和上面一樣使用
(*p4).name = "suyue"
(*p4).age = 23
fmt.Println(*p4)
}
(3)Go不支持指標運算(這點令我有點難受,可能C用的太習慣了)
(4)函式中允許回傳區域變數的地址,
Go編譯器使用“堆疊逃逸”機制將這種區域變數的空間分配在堆上,例如:
func add(a int, b int) *int{
sum := a + b
return &sun
}
1.4.2 陣列
1.陣列的定義
var name [n]elemetType
var arr [100]int //宣告了一個有100個整型資料的陣列
array := []float64{1.10, 4.54, 5.45, 9.21}
2.陣列的初始化
arr := [4]int{1, 2, 3, 4}//即指定了長度有初始化了字面量
brr := []int{1, 2, 4, 5}//不指定長度,通過初始化的數量來定義陣列的長度
crr := [3]int{1 : 1, 2 : 4}//指定長度,并通過索引值進行初始化
drr := []int{1 : 1, 2 : 3} //不知道總長度,通過索引值進行初始化
1.4.3 切片
切片的基本介紹
(1)切片的英文名時slice,
(2)切片是對資料的參考,因此切片是一個參考型別,在進行傳遞時,它遵守參考傳遞的機制,
(3)切片的長度是可變的,因此切片可以理解為一個長度可變的陣列(有點類似于C中的動態記憶體分配)
(4)切片的標準定義
var sliceName []T
var a []int
切片的基本使用
package main
import (
"fmt"
)
func main(){
//方法一:使用陣列構造
var intArr = [...]int{1, 5, 4, 8}
slice := intArr[1 : 4] //對intArr從第二個元素
//開始到第三個元素結束
fmt.Println(slice) //[5 4 8]
//使用內置函式make創建切片
a := make([]int, 5, 10) //make(type, len, cap)
fmt.Println("a 的元素: ",a) //[0, 0, 0, 0, 0]
fmt.Printf("a 的長度len = %d\n", len(a))//len = 5
fmt.Printf("a 的容量cap = %d\n", cap(a))// cap = 10
//注意:直接宣告切片變數時沒有意義的
//例如
var b []int
fmt.Printf("%v\n", b) //結果為 []
}
切片的記憶體分布

我們還能可以以結構體的形式理解:
type Slice struct{
array *int
len int
cap int
}

切片支持的操作:
(1)內置函式len()回傳切片的長度(注意這里的長度只計算已初始化的部分)
(2)內置函式cap()回傳切片的容量(容量指的是該切片所能存放資料的最大長度)
(3)內置函式append()對切片進行元素追加
(4)內置函式copy()用于切片的拷貝
a := [...] int {2, 4, 2, 6, 5, 9, 10}
b := make([]int, 5, 10)
c := a[2 : 4]
fmt.Printf("a的元素%v, 長度len=%d,容量cap=%d\n", a, len(a), cap(a))
fmt.Printf("b的元素%v\n", b)
b = append(b, 2)
b = append(b, c...)
fmt.Printf("b追加后,元素有%v\n", b)//[0 0 0 0 0 2 2 6]
d := make([]int, 2, 10)
fmt.Println(d)//[0 0]
copy(d, c)
fmt.Println(d)//[2 6]
1.4.4 map
(1)map的基本概念
map 是 key-value 資料結構,由稱欄位或者關聯陣列,類似其他編程語言的集合,在編程中經常用到
對于 map 的理解,我認為可以將其與陣列相比較,map的型別格式是:map[K]T,其中K指的是key的資料型別,T指的是valueType,陣列是通過下標來對每個資料元素進行訪問的,而下標明顯都是一個整型的資料型別,而每個下標都對應著陣列的一塊型別空間,也可以看成是一個 intKey->T 的鍵值對,而陣列下表的使用則是有語言的設計者設計好的,[ ] 中只能用整型的資料,當然有的語言也可通過字母對應的ASCLL嗎值對陣列進行訪問,如C,而 map 卻可以讓我們自己設定一個陣列的下標型別,例如:
package main
import "fmt"
func main(){
ma := map[string]int{"adv" : 1, "bffv" : 2}
fmt.Println(ma["adv"]) //"adv"->1
fmt.Println(ma["bffv"])//"bffv"->2
}
其中,要注意 slice map 和 func 型別不能作為 key 的型別
(2)map的創建
package main
import "fmt"
func main(){
//(1)map的創建
//使用字面量創建
ma := map[string]int{"a" : 1, "b" : 2}
fmt.Println(ma["a"])
fmt.Println(ma["b"])
//使用內置的make函式創建
mp1 := make(map[int]string)
mp2 := make(map[int]string, 10)
mp1[1] = "tom"
mp2[1] = "pony"
fmt.Println(mp1[1])
fmt.Println(mp2[1])
}
運行:

map 在某一方面還有了一點指標的味道 例如map[K]map[K]T,對于這種使用,我們那段代碼來體驗一下:
package main
import "fmt"
func modifUser(user map[string]map[string]string, name string){
//判斷是否由此用戶名
if user[name] != nil{
//如果有,則初始化passworld
user[name]["password"] = "you are T"
}else{
user[name] = make(map[string]string, 3)
user[name]["passworld"] = "232332323"
user[name]["nickname"] = "昵稱:" + name
}
}
func main(){
var user map[string]map[string]string
user = make(map[string]map[string]string, 10)
//注意要在對 user["sms"] 進行make, 因為 "sms" 對應的值還是 map 型別
user["sms"] = make(map[string]string, 3)
user["sms"]["pawssworld"] = "dsafad"
user["sms"]["nickname"] = "sms"
fmt.Println(user)
modifUser(user, "sms")
modifUser(user, "yrs")
fmt.Println(user)
}
運行:

(3)map 支持的操作
1.map 的單個鍵值訪問格式為 mapName[keyName],更新某個 key 的值時,mapName[keyName] 放到等號左邊,訪問某個值時放到等號右邊,
2.可以使用 range 遍歷一個 map 型別,但不能保證每次迭代元素的順序,例:
package main
import "fmt"
func main(){
mp := make(map[int]string)
mp[1] = "xzy"
mp[2] = "yrs"
mp[3] = "sy"
mp[4] = "syj"
for _, v := range mp{
fmt.Println(v)
}
}
運行:

同樣的資料每次輸出的結果順序卻不一樣,
3.洗掉 map 中的某個鍵值,可使用 delet ,語法結構是:delete(mapName, keyName),delet 是內置函式,
delete(mp, 2)
fmt.Println(len(mp)) //len = 3
1.4.5 struct
首先我們來說一下宣告是結構體,結構體其實就是將一個事物的一些或所有資訊提取出來,形成的一個新的變數,我們稱為結構提變數,所以結構體也可以說是一種自定義的變數,
我們以一個學生為例,學生身上的資訊有很多,例如:姓名、年齡、學號、性別、所在院系、身高、體重等等,我們可以將這些資訊組合在一起,構建一個名為 student 的結構體變數,那我們就選取姓名、年齡、學號、性別這幾個資訊構建一個 student 的結構體變數,代碼如下:
//構建一個 student 的結構體變數
type student struct{
name string //姓名
age int //年齡
Num int //學號
sex string //性別
}
然后我們便可以用 student 來定義變數,并對其進行資訊的填入
var Stu student
Stu.name = "yrs"
Stu.age = 20
Stu.Num = 19
Stu.sex = "male"
完整代碼如下:
package main
import "fmt"
//構建一個 student 的結構體變數
type student struct{
name string //姓名
age int //年齡
Num int //學號
sex string //性別
}
func main(){
var Stu student
Stu.name = "yrs"
Stu.age = 20
Stu.Num = 19
Stu.sex = "male"
fmt.Printf("Name:%s\n", Stu.name)
fmt.Printf("Age:%d\n", Stu.age)
fmt.Printf("Number:%d\n", Stu.Num)
fmt.Printf("Sex:%s\n", Stu.sex)
}
運行結果:

結構體在記憶體中的布局
當我們用 student 定義好一個 stu 的變數后,其在記憶體中的布局如下圖所示:

結構體的基本介紹
(1)從概念上看,結構體欄位 = 屬性 = field,
(2)欄位是結構體的一個重要組成部分,一般是基礎資料型別、陣列,也可以是參考型別,上面我們定義的 student 結構體的 name string 就是屬性,
注意事項
1.在創建一個結構體變數后,如果沒有給欄位賦值,則它們都對應這一個默認值,其中,布爾型別是 false,字串是"",指標,slice和map的默認值都是 nil,也就是說還沒分配空間,
2.不同的結構體變數的欄位是獨立的,互不影響的,
接下來我們一代碼的形式簡介一下map在結構體中的使用:
package main
import "fmt"
type Persion struct{
name string
age int
scores [5]float64
slice []int
map1 map[string]string
}
func main(){
var p1 Persion
fmt.Printf("初始化前p1->%v\n", p1)
p1.name = "yrs"
p1.age = 18
p1.scores[0] = 84.51
p1.scores[1] = 93.54
p1.scores[2] = 85.51
p1.scores[3] = 76.12
p1.scores[4] = 82.47
//使用slice一定要用make
p1.slice = make([]int, 10)
p1.slice[0] = 15
//fmt.Println(p1.slice)
//使用map要先make
p1.map1 = make(map[string]string, 10)
p1.map1["yrs"] = "rgsi"
//fmt.Println(p1.map1)
fmt.Printf("初始化后p1->%v\n", p1)
}

創建結構體變數的方式
方法式一:直接宣告
var p1 persion
方式二:
p2 := persion{"張山", 20}
方式三:指標形式
var p3 *persion = new(persion)
方式4:
var p4 *persion = &persion{}
完整案例:
package main
import "fmt"
type persion struct{
name string
age int
}
func main(){
//方式1
var p1 persion
//方式2
p2 := persion{"張山", 20}
fmt.Println(p2)
//方式3-&
var p3 *persion = new(persion)
//因為p3是指標,因此標準的給給欄位賦值方式
(*p3).name = "李四" //也可以寫成p3.name = "李四"
(*p3).age = 8439 //原因:go的設計者為了程式員方便,底層會對 p3.name = "李四" 進行處理
fmt.Println(*p3) //會給 p3 加上取值運算子 (*p3).name = "李四"
//方式4-{}
var p4 *persion = &persion{}//也可以 var p4 *persion = &persion{"suyue", 23}
//因為 p4 是一個指標,因此標準的訪問欄位的方法
//(*p4).name = ...
//也可以和上面一樣使用
(*p4).name = "suyue"
(*p4).age = 23
fmt.Println(*p4)
}

1.5 控制陳述句
1.5.1 if 陳述句
語法規則
if /*judgemen condition*/ {
//執行程式
}
//若與else連用
if {
//執行程式
}
else{
//執行程式
}
特點:
- Golang 的特點就是從簡,因此判斷條件不需要用括號括起,
- ”{“必須放在與 if 同一行上,
- if 后面可以帶一個簡單的初始化陳述句,并用分號隔開,
例如:
if a := 1;x < y {
//....
}
- Golang 沒有條件運算子(a > b ? a : b),
完整示例:
package main
import (
"fmt"
)
func main(){
var a int = 10
var b int = 32
if a > b {
fmt.Printf("a is the largest number!\n")
}else if a == b {
fmt.Printf("a is equal to the b\n")
}else{
fmt.Printf("b is the largest number!\n")
}
輸出結果:

1.5.2 switch 陳述句
switch 陳述句的作用其實和 if 、else if 的功能一樣,都是在多種條件下做出分支,但當所需的分支很多是 switch 的寫法會比if else 更簡便,
舉例:
package main
import (
"fmt"
)
func main(){
var a int = 1
switch a {
case 1:fmt.Printf("a is equal to the %d\n", 1)
case 2:fmt.Printf("a is equal to the %d\n", 2)
case 3:fmt.Printf("a is equal to the %d\n", 3)
case 4:fmt.Printf("a is equal to the %d\n", 4)
}
}
特點:
- switch 和 if 一樣,后面的簡單的初始化陳述句
- switch 后面的運算式是可選的,如果沒有運算式,則 case 子句是一個 bool 運算式,而不是一個值,
- switch 的條件運算式不像C那樣限制必須為整數,它可以是任意支持相等比較運算的型別變數,
- 通過 fallthough 陳述句來強制下一個case子句,
- switch 不用像C一樣,要與break連用,執行完某個case子句后,不會在執行下面的case子句,
1.5.3 for陳述句
我們以對應C來介紹Golang里的for陳述句:
//1,類似C里的for回圈陳述句
for init(初始值); condition(條件); post(變化){
}
//2.類似C里的while()回圈陳述句
for condition{
}
//3.類似C里的while(1)死回圈
for {
}
//4.for 還有另一種用法,是專門對陣列、切片、字串、map和通道的遍歷
//語法如下:
//訪問陣列
for index, value := range arry{
}
//訪問切片
for index, value := range slice{
}
//訪問字串
for index, value := range string{
}
//訪問map
for index, value := range map{
}
//訪問通道
for index, value := range channel{
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/300520.html
標籤:其他
上一篇:ROS從入門到放棄(學習筆記1)
下一篇:VS | 一些小細節
