Golang - 類型與聲明 (Types and Declarations)
本篇文章將介紹類型與聲明,基本上接觸過程式語言的話,許多部份是相似的,僅有少處不同。
如同其他的程式語言,Golang 也有許多與相同的類型:Boolean、Integer、Float與String,後續會分別介紹不同的類型。
但我比較喜歡使用宣告,後面若看到宣告意同聲明。(Declarations)
Zero Value
在 Golang 中,會默認 Zero Value分配給已經聲明,但未分配數值的任何變數,換句話說也可以被稱為預設值,不同的類型的 Zero Value 也有所不同。這裡列出了幾個常用的變數的 Zero Value。
【Zero Value 並不是代表為 0 !! 不同類型的變數有不同的 Zero Value !!】
Literal
Literal 簡單來說是在程式碼中賦予的數值、字元或是字串,例如:
func main(){
fmt.Println("Hello World")
}
這裡的 "Hello World"
就是字串型態的 Literal。
Integer literal
若 Literal 為整數時,透過不同的前綴代表不同的進制方式。另外,當整數字串過長時,可以透過 underscore 將數字進行分組,提高閱讀的效能,但不能在該數字的開頭或結尾使用。
0b111 // 2進制
0o111 // 8進制
0x111 // 16進制
0111 // 這也是代表8進制,但容易搞混,不要使用這種方式!!
//b,o,x可以用大寫B,O,X代替,會是一樣的作用。
a := 1111
b := 1_111 // 這兩組代表的是相同的數字。
Float literal
若 Literal 為 float 時,可以使用字母 e 和數字組合出要使用的指數數值。與 Integer Literal 一樣能使用 underscore 提高閱讀的效能。
1.11e2 // 111.0
0.111_111 // 0.111111
另外, Float Literal有一種十六進制的表示方式。以 0x 作為前綴,指數後面以字母 P 為開頭進行運算。
0x1p-2 // 1.0 * (2^-2) = 1.0/4 = 0.25
0x2.p10 // 2.0 * 2^10 = 2048.0
Rune literal
通常代表字元,會用單引號包起來,常用來表示 Unicode 字元。
'a' // Unicode
'\141' // 8進制
'\x61' // 16進制
// 另外還有一些較特別且常使用的符號。
'\n' // newLine
'\t' // tab
'\'' // 單引號
'\"' // 雙引號
'\\' // 反斜線
String literal
如同前面所提到的 "Hello world"
,會使用雙引號建立一個字串,並包含0個或多個字元,若要使用換行、反斜線與雙引號時,請記得透過反斜線進行轉義,使用方式與前面介紹 Rune literal 的符號是一樣的。
Boolean
透過 bool
表示布林變數,布林具有 true
或 false
兩種數值,bool
的 Zero Value 為 false
。
var flag bool
var status = true
Numeric Types (數值類型)
基本上可分為三類:整數、浮點數與一些不常使用的複雜類型。
整數 (integer)
基本上有各種大小的有符號數與無符號數。所有整數類型的 Zero Value 為 0 ,整數類型如下表所示:
類型名稱 | 數值範圍 |
---|---|
int8 | –128 to 127 |
int16 | –32768 to 32767 |
int32 | –2147483648 to 2147483647 |
int64 | –9223372036854775808 to 9223372036854775807 |
uint8 | 0 to 255 |
uint16 | 0 to 65536 |
uint32 | 0 to 4294967295 |
uint64 | 0 to 18446744073709551615 |
另外, int
與 uint
會因為開發環境的 CPU 而會是不同類型的整數,在32位元的CPU上會是 int32
,uint32
,在64位元的CPU上會是int64
,uint64
。
然而,這麼多種的整數類型,要如何知道在何時使用其中一個呢?可以透過下述的三條規則去決定:
- 若開發或使用在具有特定大小的整數之格式或協議,則使用相應的整數類型。
- 若是撰寫一個用在任何整數類型的函式庫之函式時,請盡可能寫兩個函式,一個用
int
而另一個使用uint
。更加嚴謹的狀況下能規定大小會更好。(e.g.int64
,uint64
) - 其他情況下,使用 int 即可,除非因為效能或是特定目的,已知需要明確的整數大小與有無符號,否則請使用
int
。
浮點數 (float)
基本上分為兩種, float32
與float64
,浮點數類型的 Zero Value 為 0。
類型名稱 | 最大絕對值 | 最小(非零) |
---|---|---|
float32 | 3.40282346638528859811704183484516925440e+38 | 1.401298464324817070923729583289916131280e-45 |
float64 | 1.797693134862315708145274237317043567981e+308 | 4.940656458412465441765687928682213723651e-324 |
除非是記憶體問題,否則請使用精度較好的 float64
,但精度較好也不能明確的表示十進制數值,請不要使用浮點數代表金錢或是需要精確十進制表示的數值。
複雜 (complex)
基本上用於複數,有complex64與complex128兩種。(想到電學的交流電計算(頭痛))。但因為複數很少使用,我認為僅要知道有這個內置的類型即可。
運算子
常見的運算子 +, -, *, /, %
,整數除法的結果仍會是整數,若需要有浮點數效果,則需使用類型轉換。
另外可以將任何運算子與 =
做結合修改變數:+=, -=, *=, /=, %=
。
比較的運算子則有 ==, !=, >=, >, <, <=
操作運算符則有:
<< // shift left
>> // shift right
& // logical AND
| // logical OR
^ // logical XOR
&^ // logical AND NOT
一樣可以與 =
結合修改變數:&=, |=, ^=, &^=, <<=, >>=
類型轉換
許多語言也有許多數字類型,且會在需要時自動類型提升,但自動轉換時可能會發生複雜的問題,並會有意想不到的結果。Go 作為嚴謹性高的語言,不允許自動類型提升,需要時要使用類型轉換才行。
var x int32 = 10
var y int64 = 20
var z int64 = int64(x) + y
var w float64 = float64(y) + float64(z)
聲明 (var, :=)
宣告變數有許多方法,而每一種方法都傳達了有關如何使用該變數的資訊。
最冗長的方法是使用 var
、類型並賦予值。
var x int = 1
若等號右側的類型,是變數的預期類型,則可以省略等號左側的類型,
var x = 1 // type is int.
若宣告變數並將其設為Zero Value,則將等號右側刪除。
var x int
並可以一次宣告多個變數,可以是相同的類型,或是不同的類型。
var x,y int = 1,2
var i,j int
var a,b = 1,"Hi"
還可以使用宣告列表(declaration list)一次宣告多個變數,但適用在函式外。
var(
x int
y = 1
a,b = 1,"Hi"
i,j string
)
在函數中時,可以使用 :=
替換掉 var
使用類型推斷的聲明。
var x = 10
x := 10 // 這兩行程式碼的作用是相同的。
一樣可以同時聲明多個變數,但這種不帶類型的宣告方式只能在函數中使用。
a,b := 1,"Hi"
:=
可以允許為現有的變數賦值,只要 :=
左側有一個新的變數,則任何其他變數都可以存在。
x := 1
x,y := 2, "Hi"
在package宣告變數的話,則必須使用 var
宣告,在函式外使用 :=
是不允許的。
另外有些情況下,盡量不要使用:=
- 將變數初始化為 Zero Value 時,請使用
var
進行宣告,因為 Zero Value 是有意的。 - 賦值的預設類型與變數的類型不同時,請使用
var
。
x := byte(20)
// 這個不好,賦值的預設類型是int,但變數是 byte。雖然可以用類型轉換處理這個議題,但用 var 直接宣告清楚會好一點。
var x byte = 20
:=
可以同時宣告新變數與現有變數,但使用上可能會有 shadow variable 的狀況發生,建議宣告新的變數時用var
宣告,並用=
賦予數值。
最後,盡量不在函式外宣告變數。會難以追蹤。
const
簡單來說,就是宣告一個數值,且該數值是不可變的。
const x int64 = 1
const (
a = 1
b = 2
)
變數命名
基本上沒有硬性規定,但還是建議遵循以下幾點。
- 當變數名稱為多個單字組成時,採用駝峰式大小寫命名。
var studentScore int = 100
- 變數名稱的第一個字母會確定是否能在 package 外被存取。
- 在函式中,盡量使用較短的變數名稱,當該變數的範圍愈小,名稱就應該愈短。(e.g.跑迴圈時常使用 i 為變數(integer))
- Go 是強類型語言,是不需要在變數名稱中加入預期的變數類型,與其加入變數類型,最好是再想一個更完整的名稱來宣告這個變數代表甚麼。