本文首發於公眾號:Hunter後端
原文鏈接:Golang基礎筆記一之變量聲明和類型介紹
這一篇筆記主要介紹 Golang 的基礎內容,包括 Golang 的運行,變量聲明以及 Golang 裏的各種數據類型。
以下是本篇筆記目錄:
- Golang 的運行
- 變量聲明
- Golang 數據類型
- fmt 函數
1、Golang 的運行
在介紹後面的函數前,我們所有的代碼都放在 main 函數裏,如果有特殊需要引入的包,這裏會提前説明。
下面我們創建一個 main.go 文件,內容如下:
// main.go
package main
import "fmt"
func main() {
fmt.Println("hello world")
}
運行的方式,我們直接使用 go run main.go,執行了之後在控制枱看到 hello world 的輸出説明我們已經運行成功了。
2、變量聲明
Go 中使用變量前必須要先聲明,聲明變量可以使用 var 來修飾,也可以使用 := 來聲明。
如果使用 := 則必須要在聲明的時候賦值,而使用 var 的時候則不需要,會自動給定該變量類型的初始值。
使用 var 聲明變量並賦值
var i int = 10
使用 var 聲明變量不賦值
var i int
上面聲明瞭 i 變量不賦值,因為聲明瞭變量類型為 int,則該變量初始化為 0。
使用 := 聲明變量
i := 10
通過這種方式聲明的變量會根據變量值自動為其獲取變量類型。
批量聲明變量
可以批量聲明多個變量:
var a, b int
批量聲明多個變量的時候賦值:
var (
a = 100
b = "456"
)
或者這樣聲明:
a, b := 123, "456"
3、Golang 的數據類型
1. 數值類型
Golang 裏數值類型有整型、浮點型等,這裏介紹整型和浮點型
1) 整型
整數類型分為兩種,有符號整數和無符號整數。
有符號整型就是是否包含負數,如果包含負數,則其範圍是對應的無符號的一半。
有符號整數:int8, int16, int32, int64 和 int
有符號整數:uint8, uint16, uint32, uint64 和 uint
int8 的整數範圍是 -2^7 ~ 2^7 -1
uint8 的整數範圍是 0 - 2^8-1
同理,對於 int16,int32,int64 都是對應的負的 2 的 n-1 次方到 2 的 n-1 次方減一。
package main
import (
"fmt"
)
func main() {
var a int8 = -128
fmt.Println(a)
var b uint8 = 255
fmt.Println(b)
}
對於我們常用的 int,它的範圍則取決於我們使用的系統,我們的系統是 32 位,則範圍是 負的 2 的 31 次方到 2 的 31 次方減一。
2) 浮點型
Go 中的浮點型有兩種,單精度 float32 和雙精度 float64,分別是 32 位浮點數和 64 位浮點數。
比如我們這樣定義:
var a float64 = 16.789
float32 可以精確到小數點後 6 位,float64 可以精確到小數點後 15 位。
可以精確到小數點後的位數是怎麼計算的,比如 float32,使用 32位表示一個浮點數,其中,1位表示符號,8位表示指數,23位表示位數,而浮點數在計算機中以二進制形式存儲,所以 23 位的位數從二進制轉為十進制精度可以如下計算:
d = n * log(2)
float32 的尾數有 23 位,大概可以精確到十進制的小數點後位數位 23 * log(2) = 6.9,大約在 6-7,所以可以提供 6-7位十進制數字的精度。
同理 float64 使用 64位表示一個浮點數,1位表示符號,11位表示指數,52位表示尾數,所以可以精確到的小數點位數是 15 位。
2. 布爾類型
布爾類型只有兩個值,true 和 false:
var a bool = true
fmt.Println(a)
3. 字符串
字符串使用雙引號包起來,下面是其定義:
var a string = "abc"
fmt.Println("a: ", a)
var b = "abc"
fmt.Println("b: ", b)
c := "abc"
fmt.Println("c: ", c)
4. 字符
字符類型有兩種,一種是 byte,一種是 rune,其實他們分別是 uint8 和 int32 的別名,分別用來表示 ASCII 字符和 Unicode 字符,使用單引號包起來。
1) byte
先來介紹 byte,前面説過,byte 實際上就是 uint8,所以雖然我們給其賦值 ASCII 字符,但是它的值本身還是一個整型:
var a byte = 'a'
fmt.Println("a: ", a)
// 輸出結果是 97
如果我們想要其輸出原本的字符內容,可以使用 fmt.Printf() 函數:
var byteContent byte = 'a'
fmt.Printf("byteContent 的內容是: %c\n", byteContent)
// byteContent 的內容是: a
2) rune
rune 本質上是 int32,所以它的使用和 byte 是一樣的:
var runeContent rune = '中'
fmt.Println("runeContent 的值是: ", runeContent)
// runeContent 的值是: 20013
fmt.Printf("runeContent 的內容是: %c\n", runeContent)
// runeContent 的內容是: 中
5. 指針
指針用於存儲變量的內存地址,我們可以通過指針訪問這個變量的內存地址,也可以通過指針訪問到這個變量的內存地址存儲的值。
使用 & 符號來獲取變量的內存地址,使用 * 獲取指針指向的內存地址的值:
var a int = 10
var a_ptr *int = &a
fmt.Println("a 的內存地址是: ", &a)
fmt.Println("a_ptr 的值是: ", a_ptr)
fmt.Println("根據指針獲取的值是: ", *a_ptr)
6. 數組
數組是具有固定長度的相同類型元素的序列。
這裏有兩個點需要注意,數組的長度是固定的,數組的元素是相同的,且在定義的時候就定好的。
var arr [3]int
arr[0] = 1
arr[1] = 2
arr[2] = 3
fmt.Println("arr: ", arr)
也可以在定義的時候直接對其賦值:
var arr [3]int = [3]int{1, 2, 3}
fmt.Println("arr: ", arr)
或者定義的時候不指定數量,自動獲取:
var arr = [...]int{1, 2, 3}
fmt.Println("arr: ", arr)
還可以在定義的時候,指定索引位置的值:
var arr = [...]string{0: "Peter", 3: "Tome", 1: "Hunter"}
fmt.Println("arr: ", arr)
7. 切片
切片是對數組的一個連續片段的引用,它本身不存儲數據,而是指向底層數組。
var arr = [...]int{1, 2, 3, 4, 5}
slice := arr[1:3]
fmt.Println("slice 內容為: ", slice)
有關於切片的更多的內容在後面筆記中會更詳細的介紹。
8. map
map 是 Golang 裏的映射:
m := make(map[string]int)
m["apple"] = 1
m["banana"] = 2
fmt.Println(m)
9. 結構體
結構體是將零個或多個任意類型的命名變量組合在一起的聚合數據類型,其定義和使用方式如下:
type Person struct {
Age int
Name string
}
func main() {
var person Person
person.Age = 25
person.Name = "Hunter"
fmt.Println("結構體 person: ", person)
}
10. 通道
goroutine 是 Go 協程,而通道(channel)是用於在不同的 goroutine 之間進行通信的工具。
創建一個通道的方式如下:
ch := make(chan int)
上面的操作表示我們創建了一個 ch 通道,可以往通道里傳入 int 型數據。
使用 <- 向通道傳入數據,或者接收數據:
ch <- x // 將 x 傳入通道
x = <- ch // 接收並將值賦給 x
<-ch // 接收語句,將結果丟棄
4、fmt 函數
Golang 的 fmt 包可以用於格式化輸入和輸出。
1. 格式化輸出函數
先介紹幾個格式化輸出函數:fmt.Print、fmt.Println、fmt.Printf。
fmt.Print() 相當於是直接輸出打印內容,而 fmt.Println() 相當於是在輸出內容末尾加上換行符 \n。
比如下面兩個輸出在格式上是等效的:
fmt.Print("123\n")
fmt.Println("123")
而 fmt.Printf() 則是對含有變量的輸出內容進行格式化處理,常見的格式化內容如下:
| 佔位符 | 輸出格式 |
|---|---|
| %s | 字符串 |
| %T | 變量類型 |
| %d | 輸出十進制 |
| %b | 輸出二進制 |
| %f | 輸出浮點數 |
| %c | 輸出變量的 unicode 值 |
| %t | 輸出布爾型 |
| %v | 輸出變量的值 |
以下是對應的示例:
var a int = 10
fmt.Printf("a 的整數值是 %d\n", a)
fmt.Printf("a 的二進制是 %b\n", a)
var b string = "this is a test"
fmt.Printf("b 的字符串輸出是 %s\n", b)
fmt.Printf("b 的值的輸出是 %v\n", b)
fmt.Printf("b 的類型是 %T\n", b)
var c float64 = 12.3456
fmt.Printf("c 浮點數輸出是 %f\n", c)
fmt.Printf("c 保留兩位小數輸出是 %.2f\n", c)
var d bool = true
fmt.Printf("d 的布爾值是 %t\n", d)
var e rune = '中'
fmt.Printf("e 的 unicode 值是 %c\n", e)
fmt 還有一個 Sprintf 函數,作用是按照指定的格式將參數格式化後並返回一個字符串:
name := "Hunter"
age := 28
message := fmt.Sprintf("My name is %s and I'm %d years old.", name, age)
fmt.Println("message: ", message)
// message: My name is Hunter and I'm 28 years old.
2. 格式化輸入函數
1) fmt.Scan
從標準輸入讀取數據,並將讀取到的數據賦值給參數,參數之間以空格或者鍵入回車鍵進行分隔,直到給定的參數都被輸入:
var num1, num2 int
fmt.Print("輸出兩個整數:")
fmt.Scan(&num1, &num2)
fmt.Printf("輸出的兩個值是 %d 和 %d\n", num1, num2)
在上面的操作中,如果我們只輸入一個數字就按了回車鍵,等待輸入的程序並不會停止,而是直到我們輸入指定個數的參數。
2) fmt.Scanln
從標準輸入讀取一行數據,將讀取到的數據賦值給參數,遇到換行符結束讀取,這個操作同樣會以空格將參數進行分隔:
var num1, num2 int
fmt.Print("輸出兩個整數:")
fmt.Scanln(&num1, &num2)
fmt.Printf("輸出的兩個值是 %d 和 %d\n", num1, num2)
在這個操作裏,即便我們只輸入了一個數字,就按下了回車鍵,等待輸入的程序也會結束,沒有輸入值的變量會使用其默認值。
3) fmt.Scanf
按照指定的格式從標準輸入讀取數據,然後賦值給參數,相對於 fmt.Scan() 和 fmt.Scanln(),fmt.Scanf() 輸入數據的分割方式更自由一點,比如如果我們想一行一個參數,可以如下操作:
var num1, num2 int
fmt.Print("輸出兩個整數:")
fmt.Scanf("%d\n%d", &num1, &num2)
fmt.Printf("輸出的兩個值是 %d 和 %d\n", num1, num2)
3. fmt.Sprint
這個函數的功能是按照指定的格式將參數格式化並返回一個字符串,也就是説我們使用這個函數對字符串進行格式化,下面是操作示例:
formatStr := "My name is %s and I'm %d years old"
message := fmt.Sprintf(formatStr, "Hunter", 28)
fmt.Println(message)