3.2 基本类型
基本类型
Go语言的基本类型并不多,主要有布尔类型、byte类型、rune类型、数字型和string类型。
我们在这里主要讲解布尔类型、数字型和string类型。
在具体讲解前要强调的是:Go 是强类型语言,因此不会进行隐式转换,任何不同类型之间的转换都必须显式说明。Go 不存在像 C 和 Java 那样的运算符重载,表达式的解析顺序是从左至右。还有就是,只有两个类型相同的值才可以进行二元运算。
一、布尔类型bool
在Go语言里面使用bool来表示布尔类型,值只能是true或者false,也就是“真”和“假”。
var b1 bool
b1 = true
b2 := false
布尔型的常量和变量也可以通过和逻辑运算符(非 !、和 &&、或 ||)结合来产生另外一个布尔值。
逻辑值可以用于条件结构中的条件语句,用于测试某个条件是否满足。另外,和 &&、或 || 、不等 !=与相等 ==属于二元运算符,而非 ! 属于一元运算符
二、数字类型
在Go中,表示数值的类型非常多,有int、int8、int16、int32、int64、float32、float64等。不同的类型之间不能够隐式转换,比如源程序
var i int
var j int32
i = 2
j = i // 编译错误:cannot use i (type int) as type int32 in assignment
我们可以通过类新转换来修正程序源程序
var i int
var j int32
i = 2
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进制。源程序
fmt.Println(076) //十进制62
fmt.Println(76) //十进制76
fmt.Println(0x76) //十进制118
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的例子,源程序
var a, b, c, d byte
a = 'A' //在 ASCII 码表中,A 的值是 65,而使用 16 进制表示则为 41,下面四种写法是等效的
b = 65
c = '\x41'
d = 0x41
fmt.Println(a, b, c, d)
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的例子源程序
package main
import "fmt"
func main() {
var r1 rune = '\u0041'
var r2 rune = '\U00000041'
fmt.Printf("%c %c\r\n", r1, r2)
r3 := '中'
r4 := '\u4E2D'
r5 := []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}
fmt.Printf("%U %c %s\r\n", r3, r4, r5)
str := []rune(string("养由基")) //string 在下一节详细介绍
for _, r := range str {
fmt.Printf("%U\r\n", r)
}
fmt.Println(string([]rune{0x517B, 0x7531, 0x57FA}))
fmt.Println(string([]rune{'\u517B', '\u7531', '\u57FA'}))
}
三、字符串类型
在go中,字符串是一种内置的类型。字符串是 UTF-8 字符的一个序列,当字符为 ASCII 码时则占用 1 个字节,其它字符根据需要占用 2-4 个字节。
字符串包括解释型和原生型(raw):
解释字符串:
该类字符串使用双引号括起来,其中的相关的转义字符将被替换,这些转义字符包括:
\n:换行符
\r:回车符
\t:tab 键
\u 或 \U:Unicode 字符
\\:反斜杠自身
非解释字符串raw:
该类字符串使用括起来,支持换行,例如:
The quick brown fox jumps over a lazy dog.\r\n中的
\r\n` 会被原样输出。
可以使用数组下标的形式获取字符,但是注意,不能用这种方式对字符进行修改。源程序
package main
func main() {
var str string //声明一个string类型的变量
str = `The quick brown fox jumps over a lazy dog.` //赋值
r := str[0] //获取第一个元素的值
length := len(str) //字符串的长度
str[0] = 's' //编译报错:cannot assign to str[0]
}
字符串的长度用len计算
字符串拼接用 +
两个字符串 s1 和 s2 可以通过 s := s1 + s2 拼接在一起。
s2 追加在 s1 尾部并生成一个新的字符串 s。
也可以使用+=来拼接 s1 += s2
在循环中使用加号 + 拼接字符串并不是最高效的做法,更好的办法是使用函数 strings.Join()。最好的方法是使用bytes.Buffer来拼接。我从网上找到了一个小例子,对这几种方式的速度进行了比较:源程序
package main
import (
"bytes"
"fmt"
"strings"
"time"
)
func benchmarkStringFunction(n int, index int) (d time.Duration) {
v := "ni shuo wo shi bu shi tai wu liao le a?"
var s string
var buf bytes.Buffer
t0 := time.Now()
for i := 0; i < n; i++ {
switch index {
case 0: // fmt.Sprintf
s = fmt.Sprintf("%s[%s]", s, v)
case 1: // string +
s = s + "[" + v + "]"
case 2: // strings.Join
s = strings.Join([]string{s, "[", v, "]"}, "")
case 3: // temporary bytes.Buffer
b := bytes.Buffer{}
b.WriteString("[")
b.WriteString(v)
b.WriteString("]")
s += b.String()
case 4: // stable bytes.Buffer
buf.WriteString("[")
buf.WriteString(v)
buf.WriteString("]")
}
if i == n-1 {
if index == 4 { // for stable bytes.Buffer
s = buf.String()
}
fmt.Println("String length:", len(s)) // consume s to avoid compiler optimization
}
}
t1 := time.Now()
d = t1.Sub(t0)
fmt.Printf("time of way(%d)=%v\n", index, d)
return d
}
func main() {
k := 5
d := [5]time.Duration{}
for i := 0; i < k; i++ {
d[i] = benchmarkStringFunction(10000, i)
}
for i := 0; i < k-1; i++ {
fmt.Printf("way %d is %6.1f times of way %d\n", i, float32(d[i])/float32(d[k-1]), k-1)
}
}