《Go 语音圣经(中文版)》
基础数据类型
Go
语言将数据类型分为四类:基础类型 、复合类型 、引用类型 和接口类型 。
整型
有int8
、int16
、int32
和int64
四种不同大小的有符号整数类型,分别对应8
、16
、32
、64
bit 大小的有符号整数,与此对应的是uint8
、uint16
、uint32
和uint64
四种无符号整数类型。
还有两种一般对应特定 CPU 平台机器字大小的有符号和无符号整数int
和uint
。其中int
是应用最广泛的数值类型。这两种类型都有同样的大小,32
或64
bit,但是我们不能对此做任何的假设,因为不同的编译器即使在相同的硬件平台上可能产生不同的大小 。
Unicode 字符rune
类型是和int32
等价的类型,通常用于表示一个 Unicode 码点。这两个名称可以互换使用。同样byte
也是uint8
类型的等价类型,byte
类型一般用于强调数值是一个原始的数据而不是一个小的整数。
还有一种无符号的整数类型uintptr
,没有指定具体的 bit 大小但是足以容纳指针。uintptr
类型只有在底层编程时才需要,特别是Go
语言和C
语言函数库或操作系统接口相交互的地 方。
二元运算符及优先级:
1 2 3 4 5 * / % << >> & &^ + - | ^ == != < <= > >= && ||
二元运算符有五种优先级。在同一个优先级,使用左优先结合规则,但是使用括号可以明确优先顺序,使用括号也可以用于提升优先级。
在Go
语言中,%
取模运算符的符号和被取模数的符号总是一致的,因此-5%3
和-5%-3
结果都是-2
。
整数除法会向着0
方向截断余数。
布尔型
、数字类型
和字符串
等基本类型都是可比较的。
1 2 3 4 5 6 & 位运算 AND | 位运算 OR ^ 位运算 XOR &^ 位清空 (AND NOT) << 左移 >> 右移
前面四个操作运算符并不区分是有符号还是无符号数。
位操作运算符&^
用于按位置零:如果对应y
中bit
位为1
的话,表达式z = x &^ y
结果z
的对应的bit
位为0
,否则z
对应的bit
位等于x
相应的bit
位的值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package mainimport "fmt" func main () { var x uint8 = 1 <<1 | 1 <<5 var y uint8 = 1 <<1 | 1 <<2 var z int8 = 1 <<1 | 1 <<5 fmt.Println(x, y, z) fmt.Printf("%08b\n" , x) fmt.Printf("%08b\n" , y) fmt.Printf("%08b\n" , z) fmt.Printf("%08b\n" , x&y) fmt.Printf("%08b\n" , x|y) fmt.Printf("%08b\n" , x^y) fmt.Printf("%08b\n" , x&^y) for i := uint (0 ); i < 8 ; i++ { if x&(1 <<i) != 0 { fmt.Println(i) } } fmt.Printf("%08b\n" , x<<1 ) fmt.Printf("%08b\n" , x>>1 ) }
结果:
1 2 3 4 5 6 7 8 9 10 11 12 34 6 34 00100010 00000110 00100010 00000010 00100110 00100100 00100000 1 5 01000100 00010001
左移运算用零填充右边空缺的 bit 位,无符号数的右移运算也是用 0 填充左边空缺的 bit 位,但是有符号数的右移运算会用符号位的值填充左边空缺的 bit 位。
浮点数到整数的转换将丢失任何小数部分,然后向数轴零方向 截断。
通常Printf
格式化字符串包含多个%
参数时将会包含对应相同数量的额外操作数,但是%
之后的[1]
副词告诉Printf
函数再次使用第一个操作数。%
后
的#
副词告诉Printf
在用%o
、%x
或%X
输出时生成0
、0x
或0X
前缀。
1 2 3 4 o := 0666 fmt.Printf("%d %[1]o %#[1]o\n" , o) x := int64 (0xdeadbeef ) fmt.Printf("%d %[1]x %#[1]x %#[1]X\n" , x)
结果如下:
1 2 438 666 0666 3735928559 deadbeef 0xdeadbeef 0XDEADBEEF
字符使用%c
参数打印,或者是用%q
参数打印带单引号的字符:
1 2 3 4 5 6 7 8 9 10 11 12 package mainimport "fmt" func main () { ascii := 'a' unicode := '国' newline := '\n' fmt.Printf("%d %[1]c %[1]q\n" , ascii) fmt.Printf("%d %[1]c %[1]q\n" , unicode) fmt.Printf("%d %[1]q\n" , newline) }
1 2 3 97 a 'a' 22269 国 '国' 10 '\n'
浮点数
一个float32
类型的浮点数可以提供大约6
个十进制数的精度,而float64
则可以提供约15
个十进制数的精度。
小数点前面或后面的数字都可能被省略,例如.707
或1.
。很小或很大的数最好用科学计数法书写,通过e
或E
来指定指数部分:
1 2 const Avogadro = 6.02214129e23 const Planck = 6.62606957e-34
用Printf
函数的%g
参数打印浮点数,将采用更紧凑的表示形式打印,并提供足够的精度,但是对应表格的数据,使用%e
(带指数)或%f
的形式打印可能更合适。所有的这三个打印形式都可以指定打印的宽度和控制打印精度。
1 2 3 4 5 6 7 8 9 10 11 12 package mainimport ( "fmt" "math" ) func main () { for x := 0 ; x < 8 ; x++ { fmt.Printf("x = %d e^x = %8.3f %8.3[2]e\n" , x, math.Exp(float64 (x))) } }
打印精度是小数点后三个小数精度和8
个字符宽度:
1 2 3 4 5 6 7 8 x = 0 e^x = 1.000 1.000e+00 x = 1 e^x = 2.718 2.718e+00 x = 2 e^x = 7.389 7.389e+00 x = 3 e^x = 20.086 2.009e+01 x = 4 e^x = 54.598 5.460e+01 x = 5 e^x = 148.413 1.484e+02 x = 6 e^x = 403.429 4.034e+02 x = 7 e^x = 1096.633 1.097e+03
正无穷大
:表示太大溢出的数字。
负无穷大
:表示除零的结果。
NAN
:非数,一般用于表示无效的除法操作结果0/0
或Sqrt(-1)
。
1 2 3 4 5 6 7 8 9 10 package mainimport ( "fmt" ) func main () { var z float64 fmt.Println(z, -z, 1 /z, -1 /z, z/z) }
函数math.IsNaN
用于测试一个数是否是非数NaN
。
测试一个结果是否是非数NaN
则是充满风险的,因为NaN
和任何数都是不相等的。
如果一个函数返回的浮点数结果可能失败,最好的做法是用单独的标志报告失败,像这样:
1 2 3 4 5 6 7 func compute () (value float64 , ok bool ) { if failed { return 0 , false } return result, true }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 package mainimport ( "fmt" "math" ) const ( width, height = 600 , 320 cells = 100 xyrange = 30.0 xyscale = width / 2 / xyrange zscale = height * 0.4 angle = math.Pi / 6 ) var sin30, cos30 = math.Sin(angle), math.Cos(angle) func main () { fmt.Printf("<svg xmlns='http://www.w3.org/2000/svg' " + "style='stroke: grey; fill: white; stroke-width: 0.7' " + "width='%d' height='%d'>" , width, height) for i := 0 ; i < cells; i++ { for j := 0 ; j < cells; j++ { ax, ay := corner(i+1 , j) bx, by := corner(i, j) cx, cy := corner(i, j+1 ) dx, dy := corner(i+1 , j+1 ) fmt.Printf("<polygon points='%g,%g %g,%g %g,%g %g,%g'/>\n" , ax, ay, bx, by, cx, cy, dx, dy) } } fmt.Println("</svg>" ) } func corner (i, j int ) (float64 , float64 ) { x := xyrange * (float64 (i)/cells - 0.5 ) y := xyrange * (float64 (j)/cells - 0.5 ) z := f(x, y) sx := width/2 + (x-y)*cos30*xyscale sy := height/2 + (x+y)*sin30*xyscale - z*zscale return sx, sy } func f (x, y float64 ) float64 { r := math.Hypot(x, y) return math.Sin(r) / r }
94/483