go 基本型別 和 運算子
1 布林型別 bool
一個簡單的例子:var b bool = true
。
布林型的值只可以是常量 true 或者 false。
兩個型別相同的值可以使用相等==
或者不等!=
運算子來進行比較並獲得一個布林型的值。
當相等運算子兩邊的值是完全相同的值的時候會返回 true,否則返回 false,並且只有在兩個的值的型別相同的情況下才可以使用。
示例:
var aVar = 10
aVar != 5 -> true
aVar != 10 -> false
Go 對於值之間的比較有非常嚴格的限制,只有兩個型別相同的值才可以進行比較,如果值的型別是介面,它們也必須都實現了相同的介面。如果其中一個值是常量,那麼另外一個值的型別必須和該常量型別相相容的。如果以上條件都不滿足,則其中一個值的型別必須在被轉換為和另外一個值的型別相同之後才可以進行比較。
布林型的常量和變數也可以通過和邏輯運算子(非!
、和&&
、或||
)結合來產生另外一個布林值,這樣的邏輯語句就其本身而言,並不是一個完整的 Go 語句。
邏輯值可以被用於條件結構中的條件語句,以便測試某個條件是否滿足。另外,和&&
、或||
與相等==
或不等!=
屬於二元運算子,而非!
屬於一元運算子。在接下來的內容中,我們會使用 T 來代表條件符合的語句,用 F 來代表條件不符合的語句。
非 !T -> false !F -> true 且 T && T -> true T && F -> false F && T -> false F && F -> false 或 T || T -> true T || F -> true F || T -> true F || F -> false
2 數字型別
2.1 整型 int 和浮點型 float
Go 語言支援整型和浮點型數字,並且原生支援複數,其中位的運算採用補碼
Go 也有基於架構的型別,例如:int、uint 和 uintptr。
這些型別的長度都是根據執行程式所在的作業系統型別所決定的:
int
和uint
在 32 位作業系統上,它們均使用 32 位(4 個位元組),在 64 位作業系統上,它們均使用 64 位(8 個位元組)。uintptr
的長度被設定為足夠存放一個指標即可。
Go 語言中沒有 float 型別。(Go語言中只有 float32 和 float64)沒有double型別。
與作業系統架構無關的型別都有固定的大小,並在型別的名稱中就可以看出來:
整數:
- int8(-128 -> 127)
- int16(-32768 -> 32767)
- int32(-2,147,483,648 -> 2,147,483,647)
- int64(-9,223,372,036,854,775,808 -> 9,223,372,036,854,775,807)
無符號整數:
- uint8(0 -> 255)
- uint16(0 -> 65,535)
- uint32(0 -> 4,294,967,295)
- uint64(0 -> 18,446,744,073,709,551,615)
浮點型(IEEE-754 標準):
- float32(+- 1e-45 -> +- 3.4 * 1e38)
- float64(+- 5 * 1e-324 -> 107 * 1e308)
int 型是計算最快的一種型別。
整型的零值為 0,浮點型的零值為 0.0。
float32 精確到小數點後 7 位,float64 精確到小數點後 15 位。由於精確度的緣故,你在使用==
或者!=
來比較浮點數時應當非常小心。你最好在正式使用前測試對於精確度要求較高的運算。
你應該儘可能地使用 float64,因為math
包中所有有關數學運算的函式都會要求接收這個型別。
你可以通過增加字首 0 來表示 8 進位制數(如:077),增加字首 0x 來表示 16 進位制數(如:0xFF),以及使用 e 來表示 10 的連乘(如: 1e3 = 1000,或者 6.022e23 = 6.022 x 1e23)。
你可以使用a := uint64(0)
來同時完成型別轉換和賦值操作,這樣 a 的型別就是 uint64。
Go 中不允許不同型別之間的混合使用,但是對於常量的型別限制非常少,因此允許常量之間的混合使用,下面這個程式很好地解釋了這個現象(該程式無法通過編譯):
package main
func main() {
var a int
var b int32
a = 15
b = a + a // 編譯錯誤
b = b + 5 // 因為 5 是常量,所以可以通過編譯
}
異常./data_types.go:66:4: cannot use a + a (type int) as type int32 in assignment
格式化說明符
在格式化字串裡,%d
用於格式化整數(%x
和%X
用於格式化 16 進製表示的數字),%g
用於格式化浮點型(%f
輸出浮點數,%e
輸出科學計數表示法),%0nd
用於規定輸出長度為n的整數,其中開頭的數字 0 是必須的。%n.mg
用於表示數字 n 並精確到小數點後 m 位,除了使用 g 之外,還可以使用 e 或者 f,例如:使用格式化字串%5.2e
來輸出 3.4 的結果為3.40e+00
。
2.2 複數
complex64 (32 位實數和虛數) complex128 (64 位實數和虛數)
複數使用re+imI
來表示,其中re
代表實數部分,im
代表虛數部分,I 代表根號負 1。
示例:
var c1 complex64 = 5 + 10i
fmt.Printf("The value is: %v", c1)
// 輸出: 5 + 10i
如果re
和im
的型別均為 float32,那麼型別為 complex64 的複數 c 可以通過以下方式來獲得:
c = complex(re, im)
函式real(c)
和imag(c)
可以分別獲得相應的實數和虛數部分。
在使用格式化說明符時,可以使用%v
來表示複數,但當你希望只表示其中的一個部分的時候需要使用%f
。
複數支援和其它數字型別一樣的運算。當你使用等號==
或者不等號!=
對複數進行比較運算時,注意對精確度的把握。cmath
包中包含了一些操作複數的公共方法。如果你對記憶體的要求不是特別高,最好使用 complex128 作為計算型別,因為相關函式都使用這個型別的引數。
3 位運算
位運算只能用於整數型別的變數,且需當它們擁有等長位模式時。
%b
是用於表示位的格式化識別符號。
二元運算子
-
按位與
&
:對應位置上的值經過和運算結果,具體參見和運算子,第 4.5.1 節,並將 T(true)替換為 1,將 F(false)替換為 0,並將 T(true)替換為 1,將 F(false)替換為 0
1 & 1 -> 1 1 & 0 -> 0 0 & 1 -> 0 0 & 0 -> 0
- 按位或
|
:
對應位置上的值經過或運算結果,具體參見或運算子,第 4.5.1 節,並將 T(true)替換為 1,將 F(false)替換為 0
1 | 1 -> 1 1 | 0 -> 1 0 | 1 -> 1 0 | 0 -> 0
- 按位異或
^
:
對應位置上的值根據以下規則組合:
1 ^ 1 -> 0 1 ^ 0 -> 1 0 ^ 1 -> 1 0 ^ 0 -> 0
位清除&^
:將指定位置上的值設定為 0。
一元運算子
-
按位補足
^
:該運算子與異或運算子一同使用,即
m^x
,對於無符號 x 使用“全部位設定為 1”,對於有符號 x 時使用m=-1
。例如:^10 = -01 ^ 10 = -11
-
位左移
<<
:-
用法:
bitP << n
。 -
bitP
的位向左移動 n 位,右側空白部分使用 0 填充;如果 n 等於 2,則結果是 2 的相應倍數,即 2 的 n 次方。例如:1 << 10 // 等於 1 KB 1 << 20 // 等於 1 MB 1 << 30 // 等於 1 GB
-
-
位右移
>>
:- 用法:
bitP >> n
。 bitP
的位向右移動 n 位,左側空白部分使用 0 填充;如果 n 等於 2,則結果是當前值除以 2 的 n 次方。
- 用法:
當希望把結果賦值給第一個運算元時,可以簡寫為a <<= 2
或者b ^= a & 0xffffffff
。
位左移常見實現儲存單位的用例
使用位左移與 iota 計數配合可優雅地實現儲存單位的常量列舉:
type ByteSize float64
const (
_ = iota // 通過賦值給空白識別符號來忽略值
KB ByteSize = 1<<(10*iota)
MB
GB
TB
PB
EB
ZB
YB
)
在通訊中使用位左移表示標識的用例
type BitFlag int
const (
Active BitFlag = 1 << iota // 1 << 0 == 1
Send // 1 << 1 == 2
Receive // 1 << 2 == 4
)
flag := Active | Send // == 3
2.4 邏輯運算子
Go 中擁有以下邏輯運算子:==
、!=
(第 4.5.1 節)、<
、<=
、>
、>=
。
它們之所以被稱為邏輯運算子是因為它們的運算結果總是為布林值bool
。例如:
b3:= 10 > 5 // b3 is true
2.5 算術運算子
常見可用於整數和浮點數的二元運算子有+
、-
、*
和/
。
(相對於一般規則而言,Go 在進行字串拼接時允許使用對運算子+
的過載,但 Go 本身不允許開發者進行自定義的運算子過載)
/
對於整數運算而言,結果依舊為整數,例如:9 / 4 -> 2
。
取餘運算子只能作用於整數:9 % 4 -> 1
。
整數除以 0 可能導致程式崩潰,將會導致執行時的恐慌狀態(如果除以 0 的行為在編譯時就能被捕捉到,則會引發編譯錯誤);第 13 章將會詳細講解如何正確地處理此類情況。
浮點數除以 0.0 會返回一個無窮盡的結果,使用+Inf
表示。
2.6 隨機數
一些像遊戲或者統計學類的應用需要用到隨機數。rand
包實現了偽隨機數的生成。
package main import ( "fmt" "math/rand" "time" ) func main() { // // 全域性函式 // rand.Seed(time.Now().Unix()) fmt.Println(rand.Int()) // int隨機值,返回值為int fmt.Println(rand.Intn(100)) // [0,100)的隨機值,返回值為int fmt.Println(rand.Int31()) // 31位int隨機值,返回值為int32 fmt.Println(rand.Int31n(100)) // [0,100)的隨機值,返回值為int32 fmt.Println(rand.Float32()) // 32位float隨機值,返回值為float32 fmt.Println(rand.Float64()) // 64位float隨機值,返回值為float64 // 如果要產生負數到正數的隨機值,只需要將生成的隨機數減去相應數值即可 fmt.Println(rand.Intn(100) - 50) // [-50, 50)的隨機值 // // Rand物件 // r := rand.New(rand.NewSource(time.Now().Unix())) fmt.Println(r.Int()) // int隨機值,返回值為int fmt.Println(r.Intn(100)) // [0,100)的隨機值,返回值為int fmt.Println(r.Int31()) // 31位int隨機值,返回值為int32 fmt.Println(r.Int31n(100)) // [0,100)的隨機值,返回值為int32 fmt.Println(r.Float32()) // 32位float隨機值,返回值為float32 fmt.Println(r.Float64()) // 64位float隨機值,返回值為float64 // 如果要產生負數到正數的隨機值,只需要將生成的隨機數減去相應數值即可 fmt.Println(r.Intn(100) - 50) // [-50, 50)的隨機值 }
3 運算子與優先順序
有些運算子擁有較高的優先順序,二元運算子的運算方向均是從左至右。下表列出了所有運算子以及它們的優先順序,由上至下代表優先順序由高到低:
優先順序 運算子 7 ^ ! 6 * / % << >> & &^ 5 + - | ^ 4 == != < <= >= > 3 <- 2 && 1 ||
當然,你可以通過使用括號來臨時提升某個表示式的整體運算優先順序。
4 類型別名
當你在使用某個型別時,你可以給它起另一個名字,然後你就可以在你的程式碼中使用新的名字(用於簡化名稱或解決名稱衝突)。
在type TZ int
中,TZ 就是 int 型別的新名稱(用於表示程式中的時區),然後就可以使用 TZ 來操作 int 型別的資料。
package main import "fmt" type TZ int func main() { var a, b TZ = 3, 4 c := a + b fmt.Printf("c has the value: %d", c) // 輸出:c has the value: 7 }
實際上,類型別名得到的新型別並非和原型別完全相同,新型別不會擁有原型別所附帶的方法;TZ 可以自定義一個方法用來輸出更加人性化的時區資訊。
5 字元型別
嚴格來說,這並不是 Go 語言的一個型別,字元只是整數的特殊用例。byte
型別是uint8
的別名,對於只佔用 1 個位元組的傳統 ASCII 編碼的字元來說,完全沒有問題。例如:var ch byte = 'A'
;字元使用單引號括起來。
在 ASCII 碼錶中,A 的值是 65,而使用 16 進製表示則為 41,所以下面的寫法是等效的:
var ch byte = 65 或 var ch byte = '\x41'
(\x
總是緊跟著長度為 2 的 16 進位制數)
另外一種可能的寫法是\
後面緊跟著長度為 3 的八進位制數,例如:\377
。
不過 Go 同樣支援 Unicode(UTF-8),因此字元同樣稱為 Unicode 程式碼點或者 runes,並在記憶體中使用 int 來表示。在文件中,一般使用格式 U+hhhh 來表示,其中 h 表示一個 16 進位制數。其實rune
也是 Go 當中的一個型別,並且是int32
的別名。
在書寫 Unicode 字元時,需要在 16 進位制數之前加上字首\u
或者\U
。
因為 Unicode 至少佔用 2 個位元組,所以我們使用int16
或者int
型別來表示。如果需要使用到 4 位元組,則會加上\U
字首;字首\u
則總是緊跟著長度為 4 的 16 進位制數,字首\U
緊跟著長度為 8 的 16 進位制數。
var ch int = '\u0041'
var ch2 int = '\u03B2'
var ch3 int = '\U00101234'
fmt.Printf("%d - %d - %d\n", ch, ch2, ch3) // integer
fmt.Printf("%c - %c - %c\n", ch, ch2, ch3) // character
fmt.Printf("%X - %X - %X\n", ch, ch2, ch3) // UTF-8 bytes
fmt.Printf("%U - %U - %U", ch, ch2, ch3) // UTF-8 code point
輸出:
65 - 946 - 1053236 A - β - r 41 - 3B2 - 101234 U+0041 - U+03B2 - U+101234
格式化說明符%c
用於表示字元;當和字元配合使用時,%v
或%d
會輸出用於表示該字元的整數;%U
輸出格式為 U+hhhh 的字串(另一個示例見第 5.4.4 節)。
包unicode
包含了一些針對測試字元的非常有用的函式(其中ch
代表字元):
- 判斷是否為字母:
unicode.IsLetter(ch)
- 判斷是否為數字:
unicode.IsDigit(ch)
- 判斷是否為空白符號:
unicode.IsSpace(ch)
這些函式返回一個布林值。包utf8
擁有更多與 rune 相關的函式。