Language GO III
本节讲述GO语言的基础数据类型
3.1. 整型
Go语言同时提供了有符号和无符号类型的整数运算。有符号整数为int8、int16、int32和int64,对应8、16、32、64bit数值。无符号为:uint8、uint16、uint32和uint64。
最常用的关键字就是int和uint,因为不同platform的不同compiler可能对应32bit或者64bit。
Unicode字符rune类型是和int32等价的类型,通常用于表示一个Unicode码点。这两个名称可以互换使用。byte类型等价于uint8类型,但byte类型一般用于强调数值是一个原始的数据而不是一个小的整数。
最后,还有一种无符号的整数类型uintptr,没有指定具体的bit大小但是足以容纳指针,用于底层编程的接口。
虽然在某些情况下,int和int32同为32bit,但是他们是不同类型,同理第二章的摄氏度华氏度类型,需要一个显式转换才能将int当作int32,反之同理。
有符号整数采用补码来表示数字大小,首位作为符号位,当首位为0时,其他为照常计算,当首位为1时,第一位为本位数字的负数,与其他位相机合并计算,故8bit的数字的值域为-128(10000000)到127(01111111)。而无符号8bit整数的大小为0到255,即8位二进制的最大值,11111111。
下面是Go语言中关于算术运算、逻辑运算和比较运算的二元运算符,它们按照优先级递减的顺序排列:
* / % << >> & &^
+ - | ^
== != < <= > >=
&&
||
具体的运算原则基本和C语言一致,不再赘述。
以下代码展示了溢出,因为unit8的最大值就是255,所以再次基础上再进行加法乘法计算就会导致计算结果溢出,具体原理要查看二进制补码计算的一些规则,此处不再赘述。
var u uint8 = 255
fmt.Println(u, u+1, u*u) // "255 0 1"
var i int8 = 127
fmt.Println(i, i+1, i*i) // "127 -128 1"
以下是逻辑(布尔)运算符:
== 等于
!= 不等于
< 小于
<= 小于等于
> 大于
>= 大于等于
以下是一元计算:
+ 一元加法 (无效果) // “+”在只有一个var的情况下是无作用的
- 负数 // “-”代表了负数
以下是bit运算:
& 位运算 AND
| 位运算 OR
^ 位运算 XOR
&^ 位清空 (AND NOT)
<< 左移
>> 右移
位操作运算符^
作为二元运算符时是按位异或(XOR),当用作一元运算符时表示按位取反;位操作运算符&^
用于按位置零(AND NOT),与非相与。
下面的代码演示了如何使用位操作解释uint8类型值的8个独立的bit位。它使用了Printf函数的%b参数打印二进制格式的数字;其中%08b中08表示打印至少8个字符宽度,不足的前缀部分用0填充。
var x uint8 = 1<<1 | 1<<5
var y uint8 = 1<<1 | 1<<2
fmt.Printf("%08b\n", x) // "00100010", the set {1, 5}
fmt.Printf("%08b\n", y) // "00000110", the set {1, 2}
fmt.Printf("%08b\n", x&y) // "00000010", the intersection {1}
fmt.Printf("%08b\n", x|y) // "00100110", the union {1, 2, 5}
fmt.Printf("%08b\n", x^y) // "00100100", the symmetric difference {2, 5}
fmt.Printf("%08b\n", x&^y) // "00100000", the difference {5}
for i := uint(0); i < 8; i++ {
if x&(1<<i) != 0 { // membership test
fmt.Println(i) // "1", "5"
}
}
fmt.Printf("%08b\n", x<<1) // "01000100", the set {2, 6}
fmt.Printf("%08b\n", x>>1) // "00010001", the set {0, 4}
在x<<n
和x>>n
移位运算中,在n这个位置上的数必须是无符号数;被操作的x可以是有符号数或无符号数。实际上,一个二进制运算中,x<<n
左移运算等价于乘以2的n次方,一个x>>n
右移运算等价于除以2的n次方。
左移运算用0补充新多出的bit位,无符号数的右移运算也是用0补充新多出的bit位,但是有符号数的右移运算会用符号位的值填充左边空缺的bit位。
虽然Go语言有无符号运算,但是实际上,日常使用中仍然是有符号运算占据主流。比如,内置的len函数返回一个有符号的int,我们可以像下面例子那样处理逆序循环。
medals := []string{"gold", "silver", "bronze"}
for i := len(medals) - 1; i >= 0; i-- {
fmt.Println(medals[i]) // "bronze", "silver", "gold"
}
如若使用无符号返回值进行上述运算,则会出现问题。因为,如果len函数返回一个无符号数,那么i会是uint类型,然后条件i >= 0
则永远为真。
正因如此,无符号运算只在少部分特殊情况中使用。
在很多场景,会遇到类似下面代码的常见的错误:
var apples int32 = 1
var oranges int16 = 2
var compote int = apples + oranges // compile error
当尝试编译这三个语句时,将产生一个错误信息:
invalid operation: apples + oranges (mismatched types int32 and int16)
这种类型不匹配的问题可以有几种不同的方法修复,将其转换为同一种类型是一个好方法:
var compote = int(apples) + int(oranges)
将浮点数转换为整数也是可以被操作的,只是会导致一些数据的丢失:
f := 3.141 // a float64
i := int(f)
fmt.Println(f, i) // "3.141 3"
f = 1.99
fmt.Println(int(f)) // "1"
f := 1e100 // a float64
i := int(f) // 结果依赖于具体实现
当使用fmt包打印一个数值时,我们可以用%d、%o或%x参数控制输出的进制格式,就像下面的例子:
o := 0666
fmt.Printf("%d %[1]o %#[1]o\n", o) // "438 666 0666"
x := int64(0xdeadbeef)
fmt.Printf("%d %[1]x %#[1]x %#[1]X\n", x)
// Output:
// 3735928559 deadbeef 0xdeadbeef 0XDEADBEEF
请注意fmt的两个使用技巧。通常Printf格式化字符串包含多个%参数时将会包含对应相同数量的额外操作数,但是%之后的[1]
副词告诉Printf函数再次使用第一个操作数。第二,%后的#
副词告诉Printf在用%o、%x或%X输出时生成0、0x或0X前缀。
字符面值通过一对单引号直接包含对应字符。最简单的例子是ASCII中类似'a'写法的字符面值,但是我们也可以通过转义的数值来表示任意的Unicode码点对应的字符,马上将会看到这样的例子。
字符使用%c
参数打印,或者是用%q
参数打印带单引号的字符:
ascii := 'a'
unicode := '国'
newline := '\n'
fmt.Printf("%d %[1]c %[1]q\n", ascii) // "97 a 'a'"
fmt.Printf("%d %[1]c %[1]q\n", unicode) // "22269 国 '国'"
fmt.Printf("%d %[1]q\n", newline) // "10 '\n'"
Comments
Post a Comment