基本类型

  Go语言的基本类型并不多,主要有布尔类型、byte类型、rune类型、数字型和string类型。

  我们在这里主要讲解布尔类型、数字型和string类型。

  在具体讲解前要强调的是:Go 是强类型语言,因此不会进行隐式转换,任何不同类型之间的转换都必须显式说明。Go 不存在像 C 和 Java 那样的运算符重载,表达式的解析顺序是从左至右。还有就是,只有两个类型相同的值才可以进行二元运算。

一、布尔类型bool

  在Go语言里面使用bool来表示布尔类型,值只能是true或者false,也就是“真”和“假”。

  1. var b1 bool
  2. b1 = true
  3. b2 := false

  布尔型的常量和变量也可以通过和逻辑运算符(非 !、和 &&、或 ||)结合来产生另外一个布尔值。

  逻辑值可以用于条件结构中的条件语句,用于测试某个条件是否满足。另外,和 &&、或 || 、不等 !=与相等 ==属于二元运算符,而非 ! 属于一元运算符

二、数字类型

  在Go中,表示数值的类型非常多,有int、int8、int16、int32、int64、float32、float64等。不同的类型之间不能够隐式转换,比如源程序

  1. var i int
  2. var j int32
  3. i = 2
  4. j = i // 编译错误:cannot use i (type int) as type int32 in assignment

我们可以通过类新转换来修正程序源程序

  1. var i int
  2. var j int32
  3. i = 2
  4. j = int32(i)
1)整型和浮点型

  在整形中,go分别支持8,16,32,64bit的有符号和无符号整形。其中 unit8 就是byte,类似于c语言中的char型。

  Go 也有基于架构的类型,例如:int、uint 和 uintptr。
这些类型的长度都是根据运行程序所在的操作系统类型所决定的:

int 和 uint 在 32 位操作系统上,它们均使用 32 位(4 个字节),在 64 位操作系统上,它们均使用 64 位(8 个字节)。

uintptr 的长度被设定为足够存放一个指针即可。

Go 语言中没有 float 类型,只有float32和float64。

float32 精确到小数点后 7 位,float64 精确到小数点后 15 位。由于精确度的缘故,你在使用 == 或者 != 来比较浮点数时应当非常小心。

你尽量使用float64,因为 math 包中所有有关数学运算的函数都会要求接收这个类型。

2)八进制和16进制

  在Go中,整数可以表示为十进制、八进制和十六进制。数值前面加上0,表示八进制;数值前面加上”0x”后者”0X”表示16进制。源程序

  1. fmt.Println(076) //十进制62
  2. fmt.Println(76) //十进制76
  3. fmt.Println(0x76) //十进制118
  4. fmt.Println(0X76) //十进制118
3)复数类型complex

  复数类型是go中引入的一种新的内置的数据类型,其中complex64(32 位实数和32虚数),complex128(64 位实数和64虚数)。

复数使用 real+imag*i 来表示,其中 real 代表实数部分,imag 代表虚数部分,i² = - 1。

复习一下数学概念里面的虚数:在数学中,虚数就是形如a+b*i的数,其中a,b是实数,且b≠0,i² = - 1。

复数的操作

内置函数complex根据一个浮点型的实部和虚部构造一个复数;函数real和imag分别获取一个复数的实部和虚部。

复数是可以比较的,两个复数a和b相等,当且仅当real(a) == real(b),imag(a) == imag(b);

三、字符类型(byte、rune)

  严格来说,这两个类型并不是 Go 语言的一个类型,字符只是整数的特殊用例。byte可以看作是uint8的别名类型,rune可以看做是int32的别名类型。(我曾经看到有人说rune是uint32的别名,这个是错误的。)
byte的例子,源程序

  1. var a, b, c, d byte
  2. a = 'A' //在 ASCII 码表中,A 的值是 65,而使用 16 进制表示则为 41,下面四种写法是等效的
  3. b = 65
  4. c = '\x41'
  5. d = 0x41
  6. fmt.Println(a, b, c, d)
  7. fmt.Printf("%c %c %c %c", a, b, c, d)

rune类型主要的作用就是为了表示Unicode类型,在这里还需要注意的是,golang中只支持UTF-8以及Unicode的编码,而对于其他的编码并没有内置的编码转换,所以在我们保存的时候需要注意。

在书写 Unicode字符时,需要在16进制数之前加上前缀\u或者\U,前缀\u后面跟着长度为4的16进制数,前缀\U紧跟着长度为8的16进制数。

rune的例子源程序

  1. package main
  2. import "fmt"
  3. func main() {
  4. var r1 rune = '\u0041'
  5. var r2 rune = '\U00000041'
  6. fmt.Printf("%c %c\r\n", r1, r2)
  7. r3 := '中'
  8. r4 := '\u4E2D'
  9. r5 := []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}
  10. fmt.Printf("%U %c %s\r\n", r3, r4, r5)
  11. str := []rune(string("养由基")) //string 在下一节详细介绍
  12. for _, r := range str {
  13. fmt.Printf("%U\r\n", r)
  14. }
  15. fmt.Println(string([]rune{0x517B, 0x7531, 0x57FA}))
  16. fmt.Println(string([]rune{'\u517B', '\u7531', '\u57FA'}))
  17. }

三、字符串类型

  在go中,字符串是一种内置的类型。字符串是 UTF-8 字符的一个序列,当字符为 ASCII 码时则占用 1 个字节,其它字符根据需要占用 2-4 个字节。

字符串包括解释型和原生型(raw):

解释字符串:

该类字符串使用双引号括起来,其中的相关的转义字符将被替换,这些转义字符包括:

  1. \n:换行符
  2. \r:回车符
  3. \ttab
  4. \u \UUnicode 字符
  5. \\:反斜杠自身
非解释字符串raw:

该类字符串使用括起来,支持换行,例如:The quick brown fox jumps over a lazy dog.\r\n中的\r\n` 会被原样输出。

可以使用数组下标的形式获取字符,但是注意,不能用这种方式对字符进行修改。源程序

  1. package main
  2. func main() {
  3. var str string //声明一个string类型的变量
  4. str = `The quick brown fox jumps over a lazy dog.` //赋值
  5. r := str[0] //获取第一个元素的值
  6. length := len(str) //字符串的长度
  7. str[0] = 's' //编译报错:cannot assign to str[0]
  8. }
字符串的长度用len计算
字符串拼接用 +

两个字符串 s1 和 s2 可以通过 s := s1 + s2 拼接在一起。

s2 追加在 s1 尾部并生成一个新的字符串 s。

也可以使用+=来拼接 s1 += s2

在循环中使用加号 + 拼接字符串并不是最高效的做法,更好的办法是使用函数 strings.Join()。最好的方法是使用bytes.Buffer来拼接。我从网上找到了一个小例子,对这几种方式的速度进行了比较:源程序

  1. package main
  2. import (
  3. "bytes"
  4. "fmt"
  5. "strings"
  6. "time"
  7. )
  8. func benchmarkStringFunction(n int, index int) (d time.Duration) {
  9. v := "ni shuo wo shi bu shi tai wu liao le a?"
  10. var s string
  11. var buf bytes.Buffer
  12. t0 := time.Now()
  13. for i := 0; i < n; i++ {
  14. switch index {
  15. case 0: // fmt.Sprintf
  16. s = fmt.Sprintf("%s[%s]", s, v)
  17. case 1: // string +
  18. s = s + "[" + v + "]"
  19. case 2: // strings.Join
  20. s = strings.Join([]string{s, "[", v, "]"}, "")
  21. case 3: // temporary bytes.Buffer
  22. b := bytes.Buffer{}
  23. b.WriteString("[")
  24. b.WriteString(v)
  25. b.WriteString("]")
  26. s += b.String()
  27. case 4: // stable bytes.Buffer
  28. buf.WriteString("[")
  29. buf.WriteString(v)
  30. buf.WriteString("]")
  31. }
  32. if i == n-1 {
  33. if index == 4 { // for stable bytes.Buffer
  34. s = buf.String()
  35. }
  36. fmt.Println("String length:", len(s)) // consume s to avoid compiler optimization
  37. }
  38. }
  39. t1 := time.Now()
  40. d = t1.Sub(t0)
  41. fmt.Printf("time of way(%d)=%v\n", index, d)
  42. return d
  43. }
  44. func main() {
  45. k := 5
  46. d := [5]time.Duration{}
  47. for i := 0; i < k; i++ {
  48. d[i] = benchmarkStringFunction(10000, i)
  49. }
  50. for i := 0; i < k-1; i++ {
  51. fmt.Printf("way %d is %6.1f times of way %d\n", i, float32(d[i])/float32(d[k-1]), k-1)
  52. }
  53. }