Go基本数据类型大详解~

89 阅读11分钟

思维导图

image.png

基本数据类型介绍

Go语言中提供了非常丰富且简单的数据类型,除了基本数据类型:整型浮点型布尔型bool字符串string等,还提供了数组切片slice结构体struct函数map通道channel等数据类型,下本文主要介绍基本数据类型~。

整型

Golang中整型如下:

数据类型描述
uint8无符号,8位整型(数据范围 0 ~ 255)
uint16无符号,16位整型(数据范围 0 ~ 65535)
uint32无符号,32位整型(数据范围 0 ~ 4294967295)
uint64无符号,64位整型(数据范围 0 ~ 18446744073709551615)
int88位整型(数据范围 -128 ~ 127)
int1616位整型(数据范围 -32768 ~ 32767)
int3232位整型(数据范围 -2147483648 ~ 2147483647)
int6464位整型(数据范围 -9223372036854775808 ~ 9223372036854775807)

上述整型数据类型中,uint8是我们熟悉的byte型,int16则对应short类型,int64对应long类型。

  • 有符号整型范围:-2^(n-1) 到 2^(n-1)-1

  • 无符号整型范围: 0 到 2^n-1

特殊整型

数据类型描述
uint32位操作系统中表示uint32,64位操作系统中表示uint64
int32位操作系统中表示int32,64位操作系统中表示int64
uintptr无符号整型,用于存放一个指针

使用intuint时,需要注意在不同操作系统下的数据范围大小。

在实际使用过程中,获取 slicemap 的元素数量等使用len()函数可以获得返回的长度,这些都是可以使用int来进行表示。但是,在涉及二进制传输、读写文件的结构描述时,为了保持文件的结构不会受到不同操作系统字节长度的影响,不要使用 intuint

浮点型

Golang中支持如下两个浮点类型如下:

数据类型描述
float32最大范围约为 3.4e38,常量定义math.MaxFloat32,精确到小数点后 7 位
float64最大范围约为 1.8e308,常量定义math.MaxFloat64,精确到小数点后 15 位

打印浮点数时,可以使用fmt包配合动词%f,同时也可以使用%0.2f指定打印时保留两位小数。

func main() {
    fmt.Printf("math.MaxFloat32: %f\n", math.Pi)
    fmt.Printf("math.MaxFloat64: %0.2f\n", math.Pi)
}
// 执行结果
math.MaxFloat32: 3.141593
math.MaxFloat64: 3.14

在格式化字符串里:

  • 格式化整数:%d
  • 格式化 16 进制表示的数字:%x 和 %X
  • 格式化浮点型:%g,
  • 输出浮点数 %f
  • 输出科学计数表示法:%e 
  • 规定输出定长的整数:%0d,其中开头的数字 0 是必须的
func main() {
    var num1 int = 255
    fmt.Printf("The number is %d\n", num1)
    fmt.Printf("The number in lowercase hex format is %x\n", num1)
    fmt.Printf("The number in uppercase hex format is %X\n", num1)
    var num2 float64 = 3.1415926
    fmt.Printf("The number in general format is %g\n", num2)
    var num3 float64 = 3.1415926
    fmt.Printf("The number in float format is %f\n", num3)
    var num4 float64 = 1234567890
    fmt.Printf("The number in scientific format is %e\n", num4)
    var num5 int = 255
    fmt.Printf("The number with leading zeros is %05d\n", num5)
}

// 执行结果
The number is 255
The number in lowercase hex format is ff
The number in uppercase hex format is FF
The number in general format is 3.1415926
The number in float format is 3.141593
The number in scientific format is 1.234568e+09
The number with leading zeros is 00255

在使用浮点数类型时,可以尽量使用float64,因为 math 包中所有有关数学运算的函数都会要求接收这个类型。例如math包下的Abs计算浮点数的绝对值。

package math

// Abs returns the absolute value of x.
//
// Special cases are:
//  Abs(±Inf) = +Inf
//  Abs(NaN) = NaN
func Abs(x float64) float64 {
    return Float64frombits(Float64bits(x) &^ (1 << 63))
}

布尔型

Go语言中,使用bool关键字声明布尔型数据的变量,布尔型数据只有true(真)false(假)两个值。声明bool类型的变量时,若未初始化则默认为false

数据类型描述
bool只有true(真)false(假)两个值,默认值为false
func main() {
    var flag bool
    fmt.Println(flag) // false
}

布尔型无法参与数值运算,也无法与其他类型进行转换。==,>,<<=>=,&&(AND),||(OR)运算符等都会产生bool值,并且&&(AND),||(OR)是具有短路行为的,如果运算符左侧已经可以确定整个布尔值表达式的值,运算符右边的值将不再被求值。

func main() {
    a, b := 5, 0
    // b != 0已确定整个布尔值,a/b > 2不会执行
    if b != 0 && a / b > 2 {
       fmt.Println("This line will not be printed")
    }

    // a/b > 2中分母b为0 产生panic,分母不能为0
    if a / b > 2 && b != 0 {
       fmt.Println("This line will not be printed")
    }
}

字符类型

数据类型描述
byte也是uint8类型的别名,代表ASCII码的一个字符
runerune 类型等价于 int32 类型,代表一个 UTF-8 字符,当需要处理中文或者其他复合字符时需要用到 rune 类型

ASCII 定义 128 个字符,由码位 0 – 127 标识。它涵盖英文字母,拉丁数字和其他一些字符,ASCII 码的一个字符占一个字节

func main() {
    var a byte = 'A'
    var b byte = 66 // 在 ASCII 码表中,B 的值是66
    var c byte = '\x43' // 67的十六进制表示为43,\x 总是紧跟着长度为 2 的 16 进制数
    var d byte = '\104' // 68的八进制表示104,用八进制定义 \后面紧跟着长度为 3 的八进制数
    fmt.Printf("%c\n", a) // A
    fmt.Printf("%c\n", b) // B
    fmt.Printf("%c\n", c) // C
    fmt.Printf("%c\n", d) // D
}

当需要处理中文或者其他复合字符时需要用到rune类型。rune类型实际是一个int32

func main() {
    str := "Hello,字符类型"
    for i := 0; i < len(str); i++ { // byte
       fmt.Printf("%v(%c) ", str[i], str[i])
    }
    fmt.Println()
    for _, r := range str { // rune
       fmt.Printf("%v(%c) ", r, r)
    }
}

// 执行结果
72(H) 101(e) 108(l) 108(l) 111(o) 239(ï) 188(¼) 140() 229(å) 173(­) 151() 231(ç) 172(¬) 166(¦) 231(ç) 177(±) 187(») 229(å) 158() 139()
72(H) 101(e) 108(l) 108(l) 111(o) 65292(,) 23383(字) 31526(符) 31867(类) 22411(型)

因为UTF8编码下一个中文汉字由3~4个字节组成,所以不能简单的按照字节去遍历一个包含中文的字符串,否则就会出现上面输出中第一行的结果。

字符串类型

在Go语言中,字符串(string)是一种基本类型,表示一系列固定长度的字符序列,以原生数据类型出现,使用字符串就像使用其他原生数据类型(int、bool、float32、float64 等)一样。

字符串被包含在双引号 "" 或反引号 `` 中,并且在内存中以UTF-8编码表示。

字符串转义符

Go 语言的字符串常见转义符包含回车、换行、单双引号、制表符等。

转义符描述
\r回车符(返回行首)
\n换行符(直接跳到下一行的同列位置)
\t制表符
\'单引号
\"双引号
\\反斜杠
func main() {
    var str string = "字符串制表符\n测试"
    fmt.Println(str)
}
// 执行结果
字符串制表符
测试

字符串特性

字符串其实是字节的定长数组,byte 和 rune 都是字符类型,若多个字符放在一起,就组成了字符串

func main() {
    var str1 string = "hello"
    var str2 [5]byte = [5]byte{104, 101, 108, 108, 111} // byte数组
    fmt.Printf("str1: %s\n", str1)
    fmt.Printf("str2: %s", str2)
}
// 执行结果
str1: hello
str2: hello

在字符串中,中文使用三个字节进行存储,而字母使用一个字节进行存储

func main() {
    var str string = "study字符串"
    fmt.Printf("str: %d\n", len(str))
}

// 执行结果, 5 + 3 * 3 = 14
str: 14

Go语言中的字符串是不可变的,这意味着一旦创建了一个字符串,就不能直接修改它的内容。但是可以通过将字符串转换为一个可变的字节数组来实现修改

func main() {
    var str string = "Study Go"
    fmt.Printf("str address: %p\n", &str)
    bytes := []byte(str)
    bytes[0] = 's'
    str = string(bytes)
    fmt.Println(str)
    fmt.Printf("str address: %p\n", &str)
}

// 执行结果
str address: 0xc000056230
study Go
str address: 0xc000056230

上述代码将字符串"Study Go"的第一个字符改为小写,并且两次打印变量str的地址。

字符串的应用

字符串所占的字节长度可以通过函数len()得到,例如上述计算str字符串的长度。

func main() {
    var str string = "study字符串"
    fmt.Printf("str: %d\n", len(str))
}

// 执行结果, 5 + 3 * 3 = 14
str: 14

如果需要知道Unicode字符串长度,可以使用utf8.RuneCountInString()函数得到。

package main

import (
    "fmt"
    "unicode/utf8"
)

func main() {
    str1 := "StudyGo"
    str2 := "学习Go"
    fmt.Printf("str1 len: %d\nd", len(str1))
    fmt.Printf("str2 len: %d\n", len(str2))
    fmt.Printf("str2 utf8.RuneCountInString: %d\n", utf8.RuneCountInString(str2))
}

// 执行结果
str1 len: 7
dstr2 len: 8
str2 utf8.RuneCountInString: 4

一般的比较运算符(==、!=、<、<=、>=、>)是通过字符串在内存中按字节逐一比较来实现字符串比较的,比较的结果是字符串自然编码的顺序。

func main() {
    // 按字节比较两个字符串
    str1 := "abc"
    str2 := "abd"
    if str1 < str2 {
       fmt.Printf("%s is less than %s\n", str1, str2)
    } else if str1 > str2 {
       fmt.Printf("%s is greater than %s\n", str1, str2)
    } else {
       fmt.Printf("%s is equal to %s\n", str1, str2)
    }
}

// 执行结果
abc is less than abd

字符串的内容(纯字节)可以通过标准索引法来获取,在方括号[]内写入索引,索引从 0 开始计数,并且只对纯 ASCII 码的字符串有效:

  • 字符串 str 的第 1 个字节:str[0]
  • 第 i 个字节:str[i - 1]
  • 最后 1 个字节:str[len(str)-1]
func main() {
    str := "Study Go"
    fmt.Printf("first: %c\n", str[0])
    i := 3
    fmt.Printf("str[i]: %c\n", str[i-1]) // 0 <= i <= len(str) - 1
    fmt.Printf("last: %c\n", str[len(str)-1])
}

// 执行结果
first: S
str[i]: u
last: o

注意:获取字符串中某个字节的地址属于非法行为,例如 &str[i]

两个字符串之间,可以通过"+"进行拼接,生成一个新的字符串。

func main() {
    str1 := "Study"
    str2 := "Go"
    str := str1 + str2
    fmt.Printf("str: %s\n", str)
}

// 执行结果
str: StudyGo

上述代码中,两个字符串 str1 和 str2 可以通过 str := str1 + str2 拼接在一起。将 str2 追加到 str1 尾部并生成一个新的字符串 str。

除了使用+进行拼接,也可以使用bytes包下的WriteString()

package main

import (
    "bytes"
    "fmt"
)

func main() {
    str1 := "Study"
    str2 := "Go"
    var stringBuilder bytes.Buffer
    stringBuilder.WriteString(str1)
    stringBuilder.WriteString(str2)
    fmt.Println(stringBuilder.String())

}

// 执行结果
StudyGo

在字符串遍历中,可以使用for rangefor 循环遍历字符串。

unicode字符集使用for range进行遍历,ascii字符集可以使用for range或者for循环遍历

package main

import (
    "fmt"
)

func main() {
    str1 := "Study Go"
    str2 := "学习Go"
    // for 遍历
    for i := 0; i < len(str1); i++ {
       fmt.Printf("ascii: %c ", str1[i])
    }
    fmt.Println()
    // for range 遍历
    for _, s := range str1 {
       fmt.Printf("unicode: %c ", s)
    }
    fmt.Println()
    for _, s := range str2 {
       fmt.Printf("unicode: %c ", s)
    }
}

// 执行结果
ascii: S ascii: t ascii: u ascii: d ascii: y ascii:   ascii: G ascii: o
unicode: S unicode: t unicode: u unicode: d unicode: y unicode:   unicode: G unicode: o
unicode: 学 unicode: 习 unicode: G unicode: o

类型转换

Go语言中不存在隐式类型转换,在Go中所有的类型转换都必须显式的声明。只有在两个类型之间支持相互转换的情况下,一个类型的值可以被转换成另一种类型的值。

类型转换的语法为:T(表达式)T表示要转换的类型。括号内的表达式包括变量、复杂算子和函数返回值等。

func main() {
    a := 10.0
    b := int(a) // 类型转换的语法 T(表达式)
    fmt.Println(b) // 10
}

类型转换只能在定义正确的情况下才能能够转换成功,例如从一个取值范围较小的类型转换到一个取值范围较大的类型(将 int16 转换为 int32),若从取值范围较大的类型转换到取值范围较小的类型时,例如 int32 转换为 int16 或将 float32 转换为 int,会发生精度丢失的情况。

func main() {
    a := 10.256
    b := int(a)    // 类型转换的语法 T()
    fmt.Println(b) // 10

    var c int32 = 255
    var d int8 = int8(c) // int8的范围-128 ~ 127
    fmt.Println(d) // -1
}

上述代码中,浮点数在转换为整型时会将小数部分去掉,只保留整数部分。

类型转换时,只有底层数据类型相同的变量才能够进行类型转换,例如int16 类型转换成 int32 类型,不同底层类型的变量进行类型转换会导致编译错误,例如 bool 类型转换为 int 类型。