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

无符号类型

有符号(可表示正负数)的整数类型:intint8int16int32int64

无符号(从0开始的取值范围)的整数类型:uintuint8uint16uint32uint64uintptr

  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+0000U+10FFFF,理论上共计1114112个码点。

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

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

中文字符的Unicode码主要集中在平面0,范围通常是U+4E00U+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 := '你'
}

布尔类型

布尔类型只有两个值truefalse,通常表示:

  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用于stringint
  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. intstring
  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. stringint
  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

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

strconv.ParseFloat

func ParseFloat(
s string,
bitSize int
) (float64, error) {
// code
}
  1. stringfloat
  2. 返回两个值,转换结果及错误,有可能转换失败。
  3. biteSize表示转换时使用3264位,返回值是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. stringbool
  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. floatstring
  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. boolstring
  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