mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2025-08-05 23:21:51 +00:00
回到简体
This commit is contained in:
@@ -1,20 +1,20 @@
|
||||
## 3.1. 整型
|
||||
|
||||
Go語言的數值類型包括幾種不同大小的整形數、浮點數和複數。每種數值類型都決定了對應的大小范圍和是否支持正負符號。讓我們先從整形數類型開始介紹。
|
||||
Go语言的数值类型包括几种不同大小的整形数、浮点数和复数。每种数值类型都决定了对应的大小范围和是否支持正负符号。让我们先从整形数类型开始介绍。
|
||||
|
||||
Go語言同時提供了有符號和無符號類型的整數運算。這里有int8、int16、int32和int64四種截然不同大小的有符號整形數類型,分别對應8、16、32、64bit大小的有符號整形數,與此對應的是uint8、uint16、uint32和uint64四種無符號整形數類型。
|
||||
Go语言同时提供了有符号和无符号类型的整数运算。这里有int8、int16、int32和int64四种截然不同大小的有符号整形数类型,分别对应8、16、32、64bit大小的有符号整形数,与此对应的是uint8、uint16、uint32和uint64四种无符号整形数类型。
|
||||
|
||||
這里還有兩種一般對應特定CPU平台機器字大小的有符號和無符號整數int和uint;其中int是應用最廣泛的數值類型。這兩種類型都有同樣的大小,32或64bit,但是我們不能對此做任何的假設;因爲不同的編譯器卽使在相同的硬件平台上可能産生不同的大小。
|
||||
这里还有两种一般对应特定CPU平台机器字大小的有符号和无符号整数int和uint;其中int是应用最广泛的数值类型。这两种类型都有同样的大小,32或64bit,但是我们不能对此做任何的假设;因为不同的编译器即使在相同的硬件平台上可能产生不同的大小。
|
||||
|
||||
Unicode字符rune類型是和int32等價的類型,通常用於表示一個Unicode碼點。這兩個名稱可以互換使用。同樣byte也是uint8類型的等價類型,byte類型一般用於強調數值是一個原始的數據而不是一個小的整數。
|
||||
Unicode字符rune类型是和int32等价的类型,通常用于表示一个Unicode码点。这两个名称可以互换使用。同样byte也是uint8类型的等价类型,byte类型一般用于强调数值是一个原始的数据而不是一个小的整数。
|
||||
|
||||
最後,還有一種無符號的整數類型uintptr,沒有指定具體的bit大小但是足以容納指針。uintptr類型隻有在底層編程是才需要,特别是Go語言和C語言函數庫或操作繫統接口相交互的地方。我們將在第十三章的unsafe包相關部分看到類似的例子。
|
||||
最后,还有一种无符号的整数类型uintptr,没有指定具体的bit大小但是足以容纳指针。uintptr类型只有在底层编程是才需要,特别是Go语言和C语言函数库或操作系统接口相交互的地方。我们将在第十三章的unsafe包相关部分看到类似的例子。
|
||||
|
||||
不管它們的具體大小,int、uint和uintptr是不同類型的兄弟類型。其中int和int32也是不同的類型,卽使int的大小也是32bit,在需要將int當作int32類型的地方需要一個顯式的類型轉換操作,反之亦然。
|
||||
不管它们的具体大小,int、uint和uintptr是不同类型的兄弟类型。其中int和int32也是不同的类型,即使int的大小也是32bit,在需要将int当作int32类型的地方需要一个显式的类型转换操作,反之亦然。
|
||||
|
||||
其中有符號整數采用2的補碼形式表示,也就是最高bit位用作表示符號位,一個n-bit的有符號數的值域是從$$-2^{n-1}$$到$$2^{n-1}-1$$。無符號整數的所有bit位都用於表示非負數,值域是0到$$2^n-1$$。例如,int8類型整數的值域是從-128到127,而uint8類型整數的值域是從0到255。
|
||||
其中有符号整数采用2的补码形式表示,也就是最高bit位用作表示符号位,一个n-bit的有符号数的值域是从$$-2^{n-1}$$到$$2^{n-1}-1$$。无符号整数的所有bit位都用于表示非负数,值域是0到$$2^n-1$$。例如,int8类型整数的值域是从-128到127,而uint8类型整数的值域是从0到255。
|
||||
|
||||
下面是Go語言中關於算術運算、邏輯運算和比較運算的二元運算符,它們按照先級遞減的順序的排列:
|
||||
下面是Go语言中关于算术运算、逻辑运算和比较运算的二元运算符,它们按照先级递减的顺序的排列:
|
||||
|
||||
```
|
||||
* / % << >> & &^
|
||||
@@ -24,13 +24,13 @@ Unicode字符rune類型是和int32等價的類型,通常用於表示一個Unic
|
||||
||
|
||||
```
|
||||
|
||||
二元運算符有五種優先級。在同一個優先級,使用左優先結合規則,但是使用括號可以明確優先順序,使用括號也可以用於提陞優先級,例如`mask & (1 << 28)`。
|
||||
二元运算符有五种优先级。在同一个优先级,使用左优先结合规则,但是使用括号可以明确优先顺序,使用括号也可以用于提升优先级,例如`mask & (1 << 28)`。
|
||||
|
||||
對於上表中前兩行的運算符,例如+運算符還有一個與賦值相結合的對應運算符+=,可以用於簡化賦值語句。
|
||||
对于上表中前两行的运算符,例如+运算符还有一个与赋值相结合的对应运算符+=,可以用于简化赋值语句。
|
||||
|
||||
算術運算符+、-、`*`和`/`可以適用與於整數、浮點數和複數,但是取模運算符%僅用於整數間的運算。對於不同編程語言,%取模運算的行爲可能併不相同。在Go語言中,%取模運算符的符號和被取模數的符號總是一致的,因此`-5%3`和`-5%-3`結果都是-2。除法運算符`/`的行爲則依賴於操作數是否爲全爲整數,比如`5.0/4.0`的結果是1.25,但是5/4的結果是1,因爲整數除法會向着0方向截斷餘數。
|
||||
算术运算符+、-、`*`和`/`可以适用与于整数、浮点数和复数,但是取模运算符%仅用于整数间的运算。对于不同编程语言,%取模运算的行为可能并不相同。在Go语言中,%取模运算符的符号和被取模数的符号总是一致的,因此`-5%3`和`-5%-3`结果都是-2。除法运算符`/`的行为则依赖于操作数是否为全为整数,比如`5.0/4.0`的结果是1.25,但是5/4的结果是1,因为整数除法会向着0方向截断余数。
|
||||
|
||||
如果一個算術運算的結果,不管是有符號或者是無符號的,如果需要更多的bit位才能正確表示的話,就説明計算結果是溢出了。超出的高位的bit位部分將被丟棄。如果原始的數值是有符號類型,而且最左邊的bit爲是1的話,那麽最終結果可能是負的,例如int8的例子:
|
||||
如果一个算术运算的结果,不管是有符号或者是无符号的,如果需要更多的bit位才能正确表示的话,就说明计算结果是溢出了。超出的高位的bit位部分将被丢弃。如果原始的数值是有符号类型,而且最左边的bit为是1的话,那么最终结果可能是负的,例如int8的例子:
|
||||
|
||||
```Go
|
||||
var u uint8 = 255
|
||||
@@ -40,7 +40,7 @@ var i int8 = 127
|
||||
fmt.Println(i, i+1, i*i) // "127 -128 1"
|
||||
```
|
||||
|
||||
兩個相同的整數類型可以使用下面的二元比較運算符進行比較;比較表達式的結果是布爾類型。
|
||||
两个相同的整数类型可以使用下面的二元比较运算符进行比较;比较表达式的结果是布尔类型。
|
||||
|
||||
```
|
||||
== equal to
|
||||
@@ -51,31 +51,31 @@ fmt.Println(i, i+1, i*i) // "127 -128 1"
|
||||
>= greater than or equal to
|
||||
```
|
||||
|
||||
事實上,布爾型、數字類型和字符串等基本類型都是可比較的,也就是説兩個相同類型的值可以用==和!=進行比較。此外,整數、浮點數和字符串可以根據比較結果排序。許多其它類型的值可能是不可比較的,因此也就可能是不可排序的。對於我們遇到的每種類型,我們需要保證規則的一致性。
|
||||
事实上,布尔型、数字类型和字符串等基本类型都是可比较的,也就是说两个相同类型的值可以用==和!=进行比较。此外,整数、浮点数和字符串可以根据比较结果排序。许多其它类型的值可能是不可比较的,因此也就可能是不可排序的。对于我们遇到的每种类型,我们需要保证规则的一致性。
|
||||
|
||||
這里是一元的加法和減法運算符:
|
||||
这里是一元的加法和减法运算符:
|
||||
|
||||
```
|
||||
+ 一元加法 (無效果)
|
||||
- 負數
|
||||
+ 一元加法 (无效果)
|
||||
- 负数
|
||||
```
|
||||
|
||||
對於整數,+x是0+x的簡寫,-x則是0-x的簡寫;對於浮點數和複數,+x就是x,-x則是x 的負數。
|
||||
对于整数,+x是0+x的简写,-x则是0-x的简写;对于浮点数和复数,+x就是x,-x则是x 的负数。
|
||||
|
||||
Go語言還提供了以下的bit位操作運算符,前面4個操作運算符併不區分是有符號還是無符號數:
|
||||
Go语言还提供了以下的bit位操作运算符,前面4个操作运算符并不区分是有符号还是无符号数:
|
||||
|
||||
```
|
||||
& 位運算 AND
|
||||
| 位運算 OR
|
||||
^ 位運算 XOR
|
||||
& 位运算 AND
|
||||
| 位运算 OR
|
||||
^ 位运算 XOR
|
||||
&^ 位清空 (AND NOT)
|
||||
<< 左移
|
||||
>> 右移
|
||||
```
|
||||
|
||||
位操作運算符`^`作爲二元運算符時是按位異或(XOR),當用作一元運算符時表示按位取反;也就是説,它返迴一個每個bit位都取反的數。位操作運算符`&^`用於按位置零(AND NOT):表達式`z = x &^ y`結果z的bit位爲0,如果對應y中bit位爲1的話,否則對應的bit位等於x相應的bit位的值。
|
||||
位操作运算符`^`作为二元运算符时是按位异或(XOR),当用作一元运算符时表示按位取反;也就是说,它返回一个每个bit位都取反的数。位操作运算符`&^`用于按位置零(AND NOT):表达式`z = x &^ y`结果z的bit位为0,如果对应y中bit位为1的话,否则对应的bit位等于x相应的bit位的值。
|
||||
|
||||
下面的代碼演示了如何使用位操作解釋uint8類型值的8個獨立的bit位。它使用了Printf函數的%b參數打印二進製格式的數字;其中%08b中08表示打印至少8個字符寬度,不足的前綴部分用0填充。
|
||||
下面的代码演示了如何使用位操作解释uint8类型值的8个独立的bit位。它使用了Printf函数的%b参数打印二进制格式的数字;其中%08b中08表示打印至少8个字符宽度,不足的前缀部分用0填充。
|
||||
|
||||
```Go
|
||||
var x uint8 = 1<<1 | 1<<5
|
||||
@@ -99,13 +99,13 @@ fmt.Printf("%08b\n", x<<1) // "01000100", the set {2, 6}
|
||||
fmt.Printf("%08b\n", x>>1) // "00010001", the set {0, 4}
|
||||
```
|
||||
|
||||
(6.5節給出了一個可以遠大於一個字節的整數集的實現。)
|
||||
(6.5节给出了一个可以远大于一个字节的整数集的实现。)
|
||||
|
||||
在`x<<n`和`x>>n`移位運算中,決定了移位操作bit數部分必須是無符號數;被操作的x數可以是有符號或無符號數。算術上,一個`x<<n`左移運算等價於乘以$$2^n$$,一個`x>>n`右移運算等價於除以$$2^n$$。
|
||||
在`x<<n`和`x>>n`移位运算中,决定了移位操作bit数部分必须是无符号数;被操作的x数可以是有符号或无符号数。算术上,一个`x<<n`左移运算等价于乘以$$2^n$$,一个`x>>n`右移运算等价于除以$$2^n$$。
|
||||
|
||||
左移運算用零填充右邊空缺的bit位,無符號數的右移運算也是用0填充左邊空缺的bit位,但是有符號數的右移運算會用符號位的值填充左邊空缺的bit位。因爲這個原因,最好用無符號運算,這樣你可以將整數完全當作一個bit位模式處理。
|
||||
左移运算用零填充右边空缺的bit位,无符号数的右移运算也是用0填充左边空缺的bit位,但是有符号数的右移运算会用符号位的值填充左边空缺的bit位。因为这个原因,最好用无符号运算,这样你可以将整数完全当作一个bit位模式处理。
|
||||
|
||||
盡管Go語言提供了無符號數和運算,卽使數值本身不可能出現負數我們還是傾向於使用有符號的int類型,就像數組的長度那樣,雖然使用uint無符號類型似乎是一個更合理的選擇。事實上,內置的len函數返迴一個有符號的int,我們可以像下面例子那樣處理逆序循環。
|
||||
尽管Go语言提供了无符号数和运算,即使数值本身不可能出现负数我们还是倾向于使用有符号的int类型,就像数组的长度那样,虽然使用uint无符号类型似乎是一个更合理的选择。事实上,内置的len函数返回一个有符号的int,我们可以像下面例子那样处理逆序循环。
|
||||
|
||||
```Go
|
||||
medals := []string{"gold", "silver", "bronze"}
|
||||
@@ -114,13 +114,13 @@ for i := len(medals) - 1; i >= 0; i-- {
|
||||
}
|
||||
```
|
||||
|
||||
另一個選擇對於上面的例子來説將是災難性的。如果len函數返迴一個無符號數,那麽i也將是無符號的uint類型,然後條件`i >= 0`則永遠爲眞。在三次迭代之後,也就是`i == 0`時,i--語句將不會産生-1,而是變成一個uint類型的最大值(可能是$$2^64-1$$),然後medals[i]表達式將發生運行時panic異常(§5.9),也就是試圖訪問一個slice范圍以外的元素。
|
||||
另一个选择对于上面的例子来说将是灾难性的。如果len函数返回一个无符号数,那么i也将是无符号的uint类型,然后条件`i >= 0`则永远为真。在三次迭代之后,也就是`i == 0`时,i--语句将不会产生-1,而是变成一个uint类型的最大值(可能是$$2^64-1$$),然后medals[i]表达式将发生运行时panic异常(§5.9),也就是试图访问一个slice范围以外的元素。
|
||||
|
||||
出於這個原因,無符號數往往隻有在位運算或其它特殊的運算場景才會使用,就像bit集合、分析二進製文件格式或者是哈希和加密操作等。它們通常併不用於僅僅是表達非負數量的場合。
|
||||
出于这个原因,无符号数往往只有在位运算或其它特殊的运算场景才会使用,就像bit集合、分析二进制文件格式或者是哈希和加密操作等。它们通常并不用于仅仅是表达非负数量的场合。
|
||||
|
||||
一般來説,需要一個顯式的轉換將一個值從一種類型轉化位另一種類型,併且算術和邏輯運算的二元操作中必須是相同的類型。雖然這偶爾會導致需要很長的表達式,但是它消除了所有和類型相關的問題,而且也使得程序容易理解。
|
||||
一般来说,需要一个显式的转换将一个值从一种类型转化位另一种类型,并且算术和逻辑运算的二元操作中必须是相同的类型。虽然这偶尔会导致需要很长的表达式,但是它消除了所有和类型相关的问题,而且也使得程序容易理解。
|
||||
|
||||
在很多場景,會遇到類似下面的代碼通用的錯誤:
|
||||
在很多场景,会遇到类似下面的代码通用的错误:
|
||||
|
||||
```Go
|
||||
var apples int32 = 1
|
||||
@@ -128,19 +128,19 @@ var oranges int16 = 2
|
||||
var compote int = apples + oranges // compile error
|
||||
```
|
||||
|
||||
當嚐試編譯這三個語句時,將産生一個錯誤信息:
|
||||
当尝试编译这三个语句时,将产生一个错误信息:
|
||||
|
||||
```
|
||||
invalid operation: apples + oranges (mismatched types int32 and int16)
|
||||
```
|
||||
|
||||
這種類型不匹配的問題可以有幾種不同的方法脩複,最常見方法是將它們都顯式轉型爲一個常見類型:
|
||||
这种类型不匹配的问题可以有几种不同的方法修复,最常见方法是将它们都显式转型为一个常见类型:
|
||||
|
||||
```Go
|
||||
var compote = int(apples) + int(oranges)
|
||||
```
|
||||
|
||||
如2.5節所述,對於每種類型T,如果轉換允許的話,類型轉換操作T(x)將x轉換爲T類型。許多整形數之間的相互轉換併不會改變數值;它們隻是告訴編譯器如何解釋這個值。但是對於將一個大尺寸的整數類型轉爲一個小尺寸的整數類型,或者是將一個浮點數轉爲整數,可能會改變數值或丟失精度:
|
||||
如2.5节所述,对于每种类型T,如果转换允许的话,类型转换操作T(x)将x转换为T类型。许多整形数之间的相互转换并不会改变数值;它们只是告诉编译器如何解释这个值。但是对于将一个大尺寸的整数类型转为一个小尺寸的整数类型,或者是将一个浮点数转为整数,可能会改变数值或丢失精度:
|
||||
|
||||
```Go
|
||||
f := 3.141 // a float64
|
||||
@@ -150,16 +150,16 @@ f = 1.99
|
||||
fmt.Println(int(f)) // "1"
|
||||
```
|
||||
|
||||
浮點數到整數的轉換將丟失任何小數部分,然後向數軸零方向截斷。你應該避免對可能會超出目標類型表示范圍的數值類型轉換,因爲截斷的行爲可能依賴於具體的實現:
|
||||
浮点数到整数的转换将丢失任何小数部分,然后向数轴零方向截断。你应该避免对可能会超出目标类型表示范围的数值类型转换,因为截断的行为可能依赖于具体的实现:
|
||||
|
||||
```Go
|
||||
f := 1e100 // a float64
|
||||
i := int(f) // 結果依賴於具體實現
|
||||
i := int(f) // 结果依赖于具体实现
|
||||
```
|
||||
|
||||
任何大小的整數字面值都可以用以0開始的八進製格式書寫,例如0666;或用以0x或0X開頭的十六進製格式書寫,例如0xdeadbeef。十六進製數字可以用大寫或小寫字母。如今八進製數據通常用於POSIX操作繫統上的文件訪問權限標誌,十六進製數字則更強調數字值的bit位模式。
|
||||
任何大小的整数字面值都可以用以0开始的八进制格式书写,例如0666;或用以0x或0X开头的十六进制格式书写,例如0xdeadbeef。十六进制数字可以用大写或小写字母。如今八进制数据通常用于POSIX操作系统上的文件访问权限标志,十六进制数字则更强调数字值的bit位模式。
|
||||
|
||||
當使用fmt包打印一個數值時,我們可以用%d、%o或%x參數控製輸出的進製格式,就像下面的例子:
|
||||
当使用fmt包打印一个数值时,我们可以用%d、%o或%x参数控制输出的进制格式,就像下面的例子:
|
||||
|
||||
```Go
|
||||
o := 0666
|
||||
@@ -170,11 +170,11 @@ fmt.Printf("%d %[1]x %#[1]x %#[1]X\n", x)
|
||||
// 3735928559 deadbeef 0xdeadbeef 0XDEADBEEF
|
||||
```
|
||||
|
||||
請註意fmt的兩個使用技巧。通常Printf格式化字符串包含多個%參數時將會包含對應相同數量的額外操作數,但是%之後的`[1]`副詞告訴Printf函數再次使用第一個操作數。第二,%後的`#`副詞告訴Printf在用%o、%x或%X輸出時生成0、0x或0X前綴。
|
||||
请注意fmt的两个使用技巧。通常Printf格式化字符串包含多个%参数时将会包含对应相同数量的额外操作数,但是%之后的`[1]`副词告诉Printf函数再次使用第一个操作数。第二,%后的`#`副词告诉Printf在用%o、%x或%X输出时生成0、0x或0X前缀。
|
||||
|
||||
字符面值通過一對單引號直接包含對應字符。最簡單的例子是ASCII中類似'a'寫法的字符面值,但是我們也可以通過轉義的數值來表示任意的Unicode碼點對應的字符,馬上將會看到這樣的例子。
|
||||
字符面值通过一对单引号直接包含对应字符。最简单的例子是ASCII中类似'a'写法的字符面值,但是我们也可以通过转义的数值来表示任意的Unicode码点对应的字符,马上将会看到这样的例子。
|
||||
|
||||
字符使用`%c`參數打印,或者是用`%q`參數打印帶單引號的字符:
|
||||
字符使用`%c`参数打印,或者是用`%q`参数打印带单引号的字符:
|
||||
|
||||
```Go
|
||||
ascii := 'a'
|
||||
|
Reference in New Issue
Block a user