Skip to main content

数据类型

语言类型​

强类型语言​

强类型语言又叫做静态语言,类型是在编译阶段确认,并且不能随意修改。

Go是典型的强类型语言。

弱类型语言​

弱类型语言与叫做动态语言,运行时进行类型推断,并且可以进行类型转换。

Go数据类型​

基本数据类型​

  1. 布尔类型。
  2. 数值类型(整型、浮点型、复数、指针)。
  3. 字符串类型

派生数据类型​

  1. 指针 *pointer
  2. 数组 Array
  3. 结构体 struct
  4. 通道 channel
  5. 切片 slice
  6. 接口 interface
  7. 映射 map
  8. 函数 func

类型分类​

值类型​

  1. 布尔类型、数值类型、字符串类型、数组类型、结构体类型。
  2. 值类型一般由内存中的栈内存进行空间分配,并且固定内存空间大小。
package main

import "fmt"

func main() {
var x int = 1
var y int = x
fmt.Println(x, y) // 1 1
}

/**
标识符 地址 内存空间
x 0x0001 1
y 0x0002 1
*/

以上过程如下:

  1. 声明变量x。
  2. 将1赋值给x。
  3. 声明变量y。
  4. 将x存储的值1拷贝一份。
  5. 将拷贝的1赋值给变量y。

引用类型​

  1. Go语言中引用类型包含指针、通道、切片、接口、映射、函数。
  2. 变量对应的栈内存空间为堆内存空间的一个地址,通过这个地址来访问堆内存对应地址的空间,从空间中取出值。
  3. 引用类型引用的是堆内存空间的地址,如果多个变量引用同一个地址,那么它们共享这些值。
  4. 引用类型一般对于内存空间的分配是动态的。

数值类型​

整型​

整型的默认类型是int,占用字节数取决于操作系统,可认为int泛指整型。

类型名字节数二进制存储位数取值范围数值范围
int432-231 ~ 231 - 1-2147483648 ~ 2147483647
int864-263 ~ 263 - 1-
int818-27 ~ 27 - 1-128 ~ 127
int16216-215 ~ 215 - 1-32768 ~ 32767
int32432-231 ~ 231 - 1-2147483648 ~ 2147483647
int64864-263 ~ 263 - 1-
package main

func main() {
var (
a int = 1
b int8 = 127
c int16 = 32767
d int32 = 2147483647
)
}

无符号类型​

有符号(可表示正负数)的整数类型:int、int8、int16、int32、int64。

无符号(从0开始的取值范围)的整数类型:uint、uint8、uint16、uint32、uint64、uintptr。

  1. 有符号和无符号的取值范围不同。
  2. 有符号内存占用略大于无符号。
  3. 使用场景不同。
  4. uintptr占用4或8个字节,无符号整型,用于存储指针,跟C语言交互。

浮点型​

浮点型的默认类型是float64。

类型名字节数二进制存储位数取值范围
float32432-3.4E38 ~ -1.4E-45, 1.4E-45 ~ 3.4E38
float64864-1.7E308 ~ -4.9E-324, 4.9E-324 ~ 1.7E308
package main

func main() {
var (
a float32 = 3.14
b float64 = 3.1415926
)
}

复数​

  1. 复数一般用于科学计算。
  2. complex64 两个float32,一个表示32位实部,一个表示32位虚部。
  3. complex128 两个float64,一个表示64位实部,一个表示64位虚部。

byte​

  1. byte是用来存放字符编码的类型,可以表示ASCII码的一个字符。
  2. byte相当于uint8,表示范围是0 ~ 255。
  3. 字符要使用单引号,字符存储本质上是字符编码的存储。
  4. ASCII码本质上就是一个字符对应一个整数的字符和数字之间的交换。
  5. byte字符的存储就是对字符对应的ASCII码进行存储。
  6. byte类型可用来缩值范围,也可以做数学运算。
package main

import "fmt"

func main() {
var (
a byte = 'a'
b uint8 = 97
)
fmt.Println(a, b) // 97 97
}

声明时,byte需要显式地指定,由于字符的默认类型是int32,因此类型推断并不好使:

package main

import "fmt"

func main() {
// var a byte = 'a'

// var a rune
var a = 'a'
fmt.Println(a)
}

ASCII码整表如下:

ASCII码

rune​

  • rune也是用来存放字符编码的类型,相当于int32。
  • rune可存储更多字符的编码,包含所有unicode码对应的字符,unicode本质就是对ASCII的扩展。
  • rune存储的是字符对应的编码,因此本质也是存储数字。
  • 在Go语言中,不存在char类型,而是把所有的字符看做是一个数字,只要这个数字能对应上ASCII或unicode码就能输出对应字符。
tip

Unicode是一个字符编码标准,为世界上几乎所有的字符集分配了唯一的数字码点。Unicode的编码范围是从U+0000到U+10FFFF,理论上共计1114112个码点。

Unicode的码点分为17个平面,每个平面有65536(2^16)个码点。其中,基本多文本平面(BMP,也称为平面0)包含了最常用的字符,范围是U+0000到U+FFFF。

由于Unicode码的最大范围是110多万,使用int32完全可以满足其存储范围,因此没必要使用uint32。

中文字符的Unicode码主要集中在平面0,范围通常是U+4E00到U+9FFF,大约20992个汉字。

package main

import "fmt"

func main() {
var ch rune = '我'
fmt.Println(ch) // 25105
fmt.Printf("%c", ch) // '我'

// var ch1 rune
var c = '中'

// var a rune
a := '你'
}

布尔类型​

布尔类型只有两个值true和false,通常表示:

  1. 是与非
  2. 真与假
  3. 对与错
  4. 成立与不成立
package main

import "fmt"

func main() {
var a bool
a = false

var b = true

c := false

fmt.Println(a, b, c)
}

字符串类型​

  1. 存储字符集合的类型。
  2. 值要用双引号。
  3. 在Go语言中,一个中文字符占3个字节。
package main

import "fmt"

func main() {
var name string
name = "Tom"

var a = "hello "

b := "world"

fullString := name + "," + a + b
stringLen := len(fullString)

fmt.Println(name, a, b, fullString, stringLen)
}

数值间转换​

  1. 数值类型之间可以相互转换,如整型转浮点型、浮点型转整型、有符号整型转无符号整型等。
  2. 浮点型转整型通常会忽略小数点,导致精度丢失。
var a = 123
var b = float32(a)

var c = 3.1415
var d = int32(c)

var e = -123
// 256 - 123 = 133
var f = uint(e)

字符串转换​

字符串相关转换操作需要使用到strconv包中的方法。

string​

使用string转换整数为一个字符串时,结果表示对应的unicode码的字符。

package main

import (
"fmt"
)

func main() {
var a = 97
var res = string(a)
fmt.Println(res) // 'a'
}

strconv.Atoi​

func Atoi(s string) (int, error) { }
  1. strconv.Atoi用于string转int。
  2. 返回两个值,转换后的结果及错误原因。
  3. 转换过程有可能不成功。
package main

import (
"fmt"
"strconv"
)

func main() {
var str = "123"

a, err := strconv.Atoi(str)

if err != nil {
fmt.Println(err)
}

fmt.Println(a)
}

strconv.Itoa​

func Itoa(i int) string { }
  1. int转string。
  2. 数字必然能转成字符串,因此无需抛出错误。
package main

import (
"fmt"
"strconv"
)

func main() {
var num = 123
a := strconv.Itoa(num)
fmt.Println(a)
}

strconv.ParseInt​

func ParseInt(
s string,
base int,
bitSize int
) (i int64, err error) {
// code
}
  1. string转int。
  2. base代表进制,bitSize代表位数。
  3. 有可能转换不成功。
package main

import (
"fmt"
"strconv"
)

func main() {
// 把 "100" 看作二进制进行十进制解析,最终输出十进制
f, err := strconv.ParseInt("100", 2, 64)
if err != nil {
fmt.Println(err)
}
fmt.Println(f)
}
tip

如果string转int中涉及到进制,则使用strconv.ParseInt,否则可直接使用strconv.Atoi。

strconv.ParseFloat​

func ParseFloat(
s string,
bitSize int
) (float64, error) {
// code
}
  1. string转float
  2. 返回两个值,转换结果及错误,有可能转换失败。
  3. biteSize表示转换时使用32或64位,返回值是float64类型。
package main

import (
"fmt"
"strconv"
)

func main() {
var str = "3.1415"
a, err := strconv.ParseFloat(str, 64)

if err != nil {
fmt.Println(err)
}

fmt.Println(a)
}

strconv.ParseBool​

func ParseBool(str string) (bool, error) {
  1. string转bool
  2. 参数可以是"1"、"0"、"false"、"true",其它值都会解析错误。
package main

import (
"fmt"
"strconv"
)

func main() {
i, err := strconv.ParseBool("false")

if err != nil {
fmt.Println(err)
}

fmt.Println(i)
}

strconv.FormatInt​

func FormatInt(i int64, base int) string { }
  1. 将十进制转换成给定的(base)进制字符串。
  2. 此过程必定会转成成功,因此无需抛出错误。
package main

import (
"fmt"
"strconv"
)

func main() {
// 将100转换成二进制并输出该字符串
str := strconv.FormatInt(100, 2)
fmt.Println(str)
}

strconv.FormatFloat​

func FormatFloat(
f float64,
fmt byte,
prec,
bitSize int
) string {}
  1. float转string。
  2. 接受四个参数:
    • f flaoat64类型的浮点数。
    • fmt 格式化类型,一般为f。
    • prec 输出精度,即小数点后保留位数,会自动四舍五入,如果是-1,则保留参数f的原有位数。
    • bitSize 32或64位数。
package main

import (
"fmt"
"strconv"
)

func main() {
str := strconv.FormatFloat(3.1415926, 'f', 4, 64)
fmt.Println(str)
}

strconv.FormatBool​

func FormatBool(b bool) string { }
  1. bool转string
  2. 参数仅接收bool类型。
package main

import (
"fmt"
"strconv"
)

func main() {
a := strconv.FormatBool(false)
fmt.Println(a)
}

自定义类型​

给一种新的类型赋予另一种已有类型的特性,让类型更加的语义化,其不能用做默认类型。

如下,将float64的特点给到类型typeAmount,并不代表两者是同一类型,它们仍然是不同的类型,因此不能直接进行相关操作,需要进行类型转换。

package main

func main() {
type typeAmount float64
var amount typeAmount = 1000.2343
var newAmount = 100.23

// invalid operation: amount - newAmount (mismatched types typeAmount and float64)
// num := amount - newAmount

num := float64(amount) - newAmount
// num := amount - typeAmount(newAmount)
fmt.Println(num)
}

获取类型​

reflect包下的Typeof方法可用来获取变量的类型:

package main

import (
"fmt"
"reflect"
)

type myType float64

func main() {
a := true
b := 123
c := "abcd"
d := 3.14
var e myType = 123

fmt.Println(reflect.TypeOf(a)) // bool
fmt.Println(reflect.TypeOf(b)) // int
fmt.Println(reflect.TypeOf(c)) // string
fmt.Println(reflect.TypeOf(d)) // float64
fmt.Println(reflect.TypeOf(e)) // myType
}

还有一种是通过reflect包的Valueof方法下的Kind方法:

func main() {
fmt.Println(reflect.ValueOf(a).Kind()) // bool
fmt.Println(reflect.ValueOf(b).Kind()) // int
fmt.Println(reflect.ValueOf(c).Kind()) // string
fmt.Println(reflect.ValueOf(d).Kind()) // float64
}

类型断言​

类型断言提供了接口值的底层具体值的访问。

v := i.(T)

这个语句断言接口值i持有具体类型T,并将底层的T值赋给变量v。如果i不持有T,该语句将触发panic。

为了测试一个接口值是否持有特定类型,类型断言可以返回两个值:底层的值和一个布尔值,用于报告断言是否成功。

package main

func mauin() {
stuInfo := map[string]interface{}{
"name": "Tom",
"age": 12,
}

name, nameOk := stuInfo["name"].(string)
age, ageOk := stuInfo["age"].(string)

fmt.Println(name, nameOk)
fmt.Println(age, ageOk)
}

如果断言成功,则返回底层值和true,否则将返回断言类型的零值和false。