目錄
- Golang | 基礎 - 3. 變數
- 3.1 變數
- 3.1.1 變數的宣告
- 3.1.2 變數初始化
- 3.1.3 變數賦值
- 3.1.4 匿名變數
- 3.2 常量
- 3.2.1 字面常量
- 3.2.2 常量定義
- 3.2.3 預定義常量
- 3.2.3.1 iota
- 3.2.3.1.1 iota只能在常量的運算式中使用
- 3.2.3.1.2 每次 const 出現時,都會讓 iota 初始化為0
- 3.2.3.1.3 自定義型別
- 3.2.3.1.4 可跳過的值
- 3.2.3.1.5 位掩碼運算式
- 3.2.3.1.6 定義數量級
- 3.2.3.1.7 中間插隊
- 3.2.3.1 iota
- 3.2.4 列舉
- 3.1 變數
Golang | 基礎 - 3. 變數
3.1 變數
變數是幾乎所有編程語言中最基本的組成元素,從根本上說,變數相當于是對一塊資料存盤空間的命名,程式可以通過定義一個變數來申請一塊資料存盤空間,之后可以通過參考變數名來使用這塊存盤空間,
Go語言中的變數使用方式與C語言接近,但具備更大的靈活性,
3.1.1 變數的宣告
Go語言的變數宣告方式與C和C++語言有明顯的不同,對于純粹的變數宣告,Go語言引入了關鍵字 var ,而型別資訊放在變數名之后,示例如下:
var v1 int
var v2 string
var v3 [10]int // 陣列
var v4 []int // 陣列切片
var v5 struct {
f int
}
var v6 *int // 指標
var v7 map[string]int // map,key為string型別,value為int型別
var v8 func(a int) int
變數宣告陳述句不需要使用分號作為結束符,與C語言相比,Go語言摒棄了陳述句必須以分號作為陳述句結束標記的習慣,
var關鍵字的另一種用法是可以將若干個需要宣告的變數放置在在一起,免得程式員需要重復寫var關鍵字,如下所示:
var (
v1 int
v2 string
)
3.1.2 變數初始化
對于宣告變數時需要進行初始化的場景, var關鍵字可以保留,但不再是必要的元素,如下所示:
var v1 int = 10 // 正確的使用方式1
var v2 = 10 // 正確的使用方式2,編譯器可以自動推匯出v2的型別
v3 := 10 // 正確的使用方式3,編譯器可以自動推匯出v3的型別
以上三種用法的效果是完全一樣的,與第一種用法相比,第三種用法需要輸入的字符數大大減少,是懶程式員和聰明程式員的最佳選擇,這里Go語言也引入了另一個C和C++中沒有的符號(冒號和等號的組合 := ),用于明確表達同時進行變數宣告和初始化的作業,
指定型別已不再是必需的,Go編譯器可以從初始化運算式的右值推匯出該變數應該宣告為哪種型別,這讓Go語言看起來有點像動態型別語言,盡管Go語言實際上是不折不扣的強型別語言(靜態型別語言),
當然,出現在 := 左側的變數不應該是已經被宣告過的,否則會導致編譯錯誤,比如下面這個寫法:
var i int
i := 2
會導致類似如下的編譯錯誤:
no new variables on left side of :=
3.1.3 變數賦值
在Go語法中,變數初始化和變數賦值是兩個不同的概念,下面為宣告一個變數之后的賦值程序:
var v10 int
v10 = 123
Go語言的變數賦值與多數語言一致,但Go語言中提供了C/C++程式員期盼多年的多重賦值功能,比如下面這個交換i和j變數的陳述句:
i, j = j, i
在不支持多重賦值的語言中,互動兩個變數的內容需要引入一個中間變數:
t = i;
i = j;
j = t;
多重賦值的特性在Go語言庫的實作中也被使用得相當充分,在介紹函式的多重回傳值時,將對其進行更加深入的介紹,總而言之,多重賦值功能讓Go語言與C/C++語言相比可以非常明顯地減少代碼行數,
3.1.4 匿名變數
我們在使用傳統的強型別語言編程時,經常會出現這種情況,即在呼叫函式時為了獲取一個值,卻因為該函式回傳多個值而不得不定義一堆沒用的變數,在Go中這種情況可以通過結合使用多重回傳和匿名變數來避免這種丑陋的寫法,讓代碼看起來更加優雅,
假設 GetName() 函式的定義如下,它回傳3個值,分別為firstName, lastName 和nickName:
func GetName() (firstName, lastName, nickName string) {
return "May", "Chan", "Chibi Maruko"
}
若只想獲得 nickName ,則函式呼叫陳述句可以用如下方式撰寫:
_, _, nickName := GetName()
這種用法可以讓代碼非常清晰,基本上屏蔽掉了可能混淆代碼閱讀者視線的內容,從而大幅降低溝通的復雜度和代碼維護的難度,
3.2 常量
在Go語言中,常量是指編譯期間就已知且不可改變的值,常量可以是數值型別(包括整型、
浮點型和復數型別)、布爾型別、字串型別等,
3.2.1 字面常量
所謂字面常量(literal),是指程式中硬編碼的常量,如:
-12
3.14159265358979323846 // 浮點型別的常量
3.2+12i // 復數型別的常量
true // 布爾型別的常量
"foo" // 字串常量
在其他語言中,常量通常有特定的型別,比如 -12 在C語言中會認為是一個 int 型別的常量,如果要指定一個值為 -12 的 long 型別常量,需要寫成 -12l ,這有點違反人們的直觀感覺,Go語言的字面常量更接近我們自然語言中的常量概念,它是無型別的,只要這個常量在相應型別的值域范圍內,就可以作為該型別的常量,比如上面的常量 -12 ,它可以賦值給 int 、 uint 、 int32 、int64 、 float32 、 float64 、 complex64 、 complex128 等型別的變數,
3.2.2 常量定義
通過 const 關鍵字,你可以給字面常量指定一個友好的名字:
const Pi float64 = 3.14159265358979323846
const zero = 0.0 // 無型別浮點常量
const (
size int64 = 1024
eof = -1 // 無型別整型常量
)
const u, v float32 = 0, 3 // u = 0.0, v = 3.0,常量的多重賦值
const a, b, c = 3, 4, "foo" // a = 3, b = 4, c = "foo", 無型別整型和字串常量
Go的常量定義可以限定常量型別,但不是必需的,如果定義常量時沒有指定型別,那么它與字面常量一樣,是無型別常量,
常量定義的右值也可以是一個在編譯期運算的常量運算式,比如
const mask = 1 << 3
由于常量的賦值是一個編譯期行為,所以右值不能出現任何需要運行期才能得出結果的運算式,比如試圖以如下方式定義常量就會導致編譯錯誤:
const Home = os.GetEnv("HOME")
原因很簡單, os.GetEnv() 只有在運行期才能知道回傳結果,在編譯期并不能確定,所以
無法作為常量定義的右值,
3.2.3 預定義常量
Go語言預定義了這些常量: true 、 false 和 iota ,
iota 比較特殊,可以被認為是一個可被編譯器修改的常量,在每一個 const 關鍵字出現時被重置為0,然后在下一個 const 出現之前,每出現一次 iota ,其所代表的數字會自動增1,
從以下的例子可以基本理解 iota 的用法:
const ( // iota被重設為0
c0 = iota // c0 == 0
c1 = iota // c1 == 1
c2 = iota // c2 == 2
)
const (
a = 1 << iota // a == 1 (iota在每個const開頭被重設為0)
b = 1 << iota // b == 2
c = 1 << iota // c == 4
)
const (
u = iota * 42 // u == 0
v float64 = iota * 42 // v == 42.0
w = iota * 42 // w == 84
)
const x = iota // x == 0 (因為iota又被重設為0了)
const y = iota // y == 0 (同上)
如果兩個 const 的賦值陳述句的運算式是一樣的,那么可以省略后一個賦值運算式,因此,上面的前兩個 const 陳述句可簡寫為:
const ( // iota被重設為0
c0 = iota // c0 == 0
c1 // c1 == 1
c2 // c2 == 2
)
const (
a = 1 <<iota // a == 1 (iota在每個const開頭被重設為0)
b // b == 2
c // c == 4
)
3.2.3.1 iota
iota 是 golang 語言的常量計數器,只能在常量的運算式中使用,
使用 iota 能簡化定義,在定義列舉時很有用,
舉例:
3.2.3.1.1 iota只能在常量的運算式中使用
fmt.Println(iota)
編譯錯誤: undefined: iota
3.2.3.1.2 每次 const 出現時,都會讓 iota 初始化為0
const a = iota // a=0
const (
b = iota //b=0
c //c=1
)
3.2.3.1.3 自定義型別
自增長常量經常包含一個自定義列舉型別,允許你依靠編譯器完成自增設定,
type Stereotype intconst (
TypicalNoob Stereotype = iota // 0
TypicalHipster // 1
TypicalUnixWizard // 2
TypicalStartupFounder // 3
)
3.2.3.1.4 可跳過的值
設想你在處理消費者的音頻輸出,音頻可能無論什么都沒有任何輸出,或者它可能是單聲道,立體聲,或是環繞立體聲的,
這可能有些潛在的邏輯定義沒有任何輸出為 0,單聲道為 1,立體聲為 2,值是由通道的數量提供,
所以你給 Dolby 5.1 環繞立體聲什么值,
一方面,它有6個通道輸出,但是另一方面,僅僅 5 個通道是全帶寬通道(因此 5.1 稱號 - 其中 .1 表示的是低頻效果通道),
不管怎樣,我們不想簡單的增加到 3,
我們可以使用下劃線跳過不想要的值,
type AudioOutput intconst (
OutMute AudioOutput = iota // 0
OutMono // 1
OutStereo // 2
_
_
OutSurround // 5
)
3.2.3.1.5 位掩碼運算式
type Allergen int
const (
IgEggs Allergen = 1 << iota // 1 << 0 which is 00000001
IgChocolate // 1 << 1 which is 00000010
IgNuts // 1 << 2 which is 00000100
IgStrawberries // 1 << 3 which is 00001000
IgShellfish // 1 << 4 which is 00010000
)
這個作業是因為當你在一個 const 組中僅僅有一個標示符在一行的時候,它將使用增長的 iota 取得前面的運算式并且再運用它,
在 Go 語言的 spec 中, 這就是所謂的隱性重復最后一個非空的運算式串列,如果你對雞蛋,巧克力和海鮮過敏,把這些 bits 翻轉到 “on” 的位置(從左到右映射 bits),然后你將得到一個 bit 值 00010011,它對應十進制的 19,
fmt.Println(IgEggs | IgChocolate | IgShellfish)
// output:
// 19
3.2.3.1.6 定義數量級
type ByteSize float64
const (
_ = iota // ignore first value by assigning to blank identifier
KB ByteSize = 1 << (10 * iota) // 1 << (10*1)
MB // 1 << (10*2)
GB // 1 << (10*3)
TB // 1 << (10*4)
PB // 1 << (10*5)
EB // 1 << (10*6)
ZB // 1 << (10*7)
YB // 1 << (10*8)
)
3.2.3.1.7 中間插隊
const (
i = iota
j = 3.14
k = iota
l
)
那么列印出來的結果是 i=0,j=3.14,k=2,l=3
3.2.4 列舉
列舉指一系列相關的常量,比如下面關于一個星期中每天的定義,通過上一節的例子,我們看到可以用在 const 后跟一對圓括號的方式定義一組常量,這種定義法在Go語言中通常用于定義列舉值,Go語言并不支持眾多其他語言明確支持的 enum 關鍵字,
下面是一個常規的列舉表示法,其中定義了一系列整型常量:
const (
Sunday = iota
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
numberOfDays // 這個常量沒有匯出
)
同Go語言的其他符號(symbol)一樣,以大寫字母開頭的常量在包外可見,
以上例子中 numberOfDays 為包內私有,其他符號則可被其他包訪問,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/5011.html
標籤:Go
