目录

3.2 基本类型

优质
小牛编辑
129浏览
2023-12-01

基本类型

  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)
    }
}