Go语言数据类型

Go语言的数据类型十分丰富,常见的包括整型、浮点型、字符串和布尔型等。Go语言特有的数据类型包括接口和通道等。本节将会对Go语言中常用的数据类型进行讲解。

整型

  • 整型主要分为有符号和无符号整型两大类:

    • 有符号整型:int8、int16、int32、int64

    • 无符号整型:uint8、uint16、uint32、uint64

  • 有符号整型其二进制最高位存储符号,因此两者的区别就是无符号整型可以存放的正数范围比有符号整型的正数范围大一倍。

    • int16 的范围为-32768( 即 -215 ) 到32768 ( 即 215 -1 )。

    • uint16 的范围为 0 到 65535( 即 216 -1 )。

编写代码🖋

package main

import "fmt"

func main() {
	a := 3
	b := 2
	fmt.Println(a / b)
}

运行结果

1

注意:在Go语言中,对于两个整型变量的除法运算,小数部分将会直接被截取,只取整数部分,不会存在四舍五入的情况。

浮点型

  • 编程语言中的浮点数就是我们常说的小数。

  • Go语言支持两种浮点数:float32float64。float32浮点数的最大范围约为3.4e38,float64浮点数最大范围约为1.8e308

  • 那么对于上面整型,我们可以用以下方式获取除法计算的精确值

编写代码🖋

package main

import (
	"fmt"
	"reflect"
)

func main() {
	a := 3.0
	b := 2.0
	fmt.Println(a / b)
	fmt.Println("变量a的类型为:", reflect.TypeOf(a))
	fmt.Println("变量a的类型为:", reflect.TypeOf(b))
}

运行结果

1.5
变量a的类型为: float64
变量a的类型为: float64
  • 此处,我们调用reflect.TypeOf()函数来打印变量a和b的类型。

  • 短变量声明并初始化 小节中,我们提到过短变量声明并初始化的方法。由于代码所在计算机为64位系统,我们通过赋值时带上小数点来告诉编译器该变量类型为float64,这样除法得到的结果也是float64类型,即可以显示出精确结果。

字符串

  • Go语言中,字符串的值为双引号中的内容,而且可以直接输入中文。

编写代码🖋

package main

import (
	"fmt"
)

func main() {
	str := "你好,小帕鲁"
	fmt.Println(str)
}

运行结果

你好,小帕鲁
  • 实际编程中,我们会遇到输入多行字符串的情况,此时需要使用 ` 字符,即反引号。

编写代码🖋

package main

import (
	"fmt"
)

func main() {
	str := `这是第一行
这是第二行
这是第三行`
	fmt.Println(str)
}

运行结果

这是第一行
这是第二行
这是第三行

字符和转义字符

1. 字符

  • 字符串中的每个元素就是字符。

  • Go 语言中,字符的值为单引号中的内容,而且可以直接输入中文。

  • Go 语言的字符有一下两种类型:

    • uint8 类型: 代表了 https://baike.baidu.com/item/ASCII/309296 码的一个字符。

    • rune类型:代表了UTF格式的一个字符(如中文、日文或其他复合字符),本质是int32类型。

编写代码🖋

package main

import "fmt"

func main() {
	english := 'a'
	chinese := '我'
	fmt.Println(english)
	fmt.Println(chinese)
}

运行结果

97
25105

PS: 第一行打印了字符“a”的ASCII码值97,第二行打印了中文“我”的int32类型值25105。

2. 转义字符

  • 通常我们使用反斜线 \ 来对字符进行转义,转义字符具有特定的含义,不同于字符原有的意义,所以称为转义字符。

  • 常见的转义字符如表所示:

转义符

含义

\n

匹配换行符

\r

匹配回车符

\t

匹配制表符

\'

匹配单引号

\"

匹配双引号

\\

匹配反斜杠

布尔型

  • 布尔型是最简单的数据类型,只有两个值:false(假)和true(真)。

编写代码🖋

package main

import "fmt"

func main() {
	var a bool
	fmt.Println(a)
	a = true
	fmt.Println(a)
}

运行结果

false
true

PS: bool 布尔

数据类型判断

  • 如果我们需要判断变量的类型,可以使用Go语言标准库中的reflect包,通过将变量传入此包的TypeOf()方法,得到变量的数据类型。

编写代码🖋

package main

import (
	"fmt"
	"reflect"
)

func main() {
	a := 1
	b := "test"
	c := true
	fmt.Println(reflect.TypeOf(a))
	fmt.Println(reflect.TypeOf(b))
	fmt.Println(reflect.TypeOf(c))
}

运行结果

int
string
bool

PS: TypeOf()方法直接返回传入变量的类型,通过Println()方法打印到控制台。

数据类型转换

  • Go语言常见的数据类型之间能够互相进行类型转换,通过使用类型前置加小括号的方式进行。

编写代码🖋

package main

import (
	"fmt"
	"reflect"
)

func main() {
	var a int16 = 97
	fmt.Println("变量 a 值为:", a, ",变量类型为:", reflect.TypeOf(a))
	b := int32(a)
	fmt.Println("变量 b 值为:", b, ",变量类型为:", reflect.TypeOf(b))
	fmt.Println("转换变量 b 类型为 string:", string(b))
}

运行结果

变量 a 值为: 97 ,变量类型为: int16
变量 b 值为: 97 ,变量类型为: int32
转换变量 b 类型为 string: a
  • 在转换变量类型时,我们需要注意变量原本的值是否会发生改变。

编写代码🖋

package main

import (
	"fmt"
	"reflect"
)

func main() {
	var a int32 = 1234567891
	fmt.Println("变量 a 值为:", a, ",变量类型为:", reflect.TypeOf(a))
	fmt.Println("转换变量 a 类型为 int16,变量 a 值变为:", int16(a), ",变量 a 类型变为:", reflect.TypeOf(int16(a)))
}

运行结果

变量 a 值为: 1234567891 ,变量类型为: int32

转换变量 a 类型为 int16,变量 a 值变为: 723 ,变量 a 类型变为: int16
  • 由于16位有符号整型的范围为-32768~32767,而变量a的值1234567891不在这个范围内,导致变量a原本的值发生改变。

  • 1234567891对应的十六进制为0x499602d3,转变为16位变量后,长度缩短一半,丢失了前(高)4位十六进制,即变为:0x02d3,其对应的十进制值为723

  • 我懵逼了

指针

谈及指针,很多人可能马上会联想到C/C++中的指针,指针的存在是C/C++强大的根本所在,但同时也带来很多安全问题,相比之下Go语言的指针则更加高效和安全。

声明指针

  • 指针是一种地址值,这个地址值代表着计算机内存空间中的某个位置。指针变量就是存放地址值的变量。

  • 一般情况下,我们将指针变量的类型声明为*int,变量名为“p”开头(指代“point”)的单词,如“p”或“ptr”。

    • 指针变量的声明格式如下:

var 变量名 *int

编写代码🖋

package main

import "fmt"

func main() {
	var p *int
	fmt.Println(p)
}

运行结果

<nil>
  • 由于指针变量未指向任何地址,所以打印值为nil。

取变量地址

  • Go语言中,使用操作符 & 取变量地址,取得的地址值可以赋给指针变量。

  • 由于指针变量本身也是变量,因此指针变量在计算机内存中也有自己的地址。

编写代码🖋

package main

import "fmt"

func main() {
	num := 1
	var p *int
	p = &num
	fmt.Println("num变量地址为:", p)
	fmt.Println("指针变量 p 的地址为:", &p)
}

运行结果

num变量地址为: 0x1400000e0a0
指针变量 p 的地址为: 0x1400004c020

PS: 需注意的是,读者运行上述程序时得到的实际结果可能与以上结果不符,甚至多次运行该程序得到的结果可能都不一致,这是由于变量在内存中的位置都是随机分配的。

获取指针所指向的内容

  • 指针变量存储的值为地址值,通过在指针变量前面加上 * 符号可以获取指针所指向地址值的内容。

编写代码🖋

package main

import "fmt"

func main() {
	num := 1
	var p *int
	p = &num
	fmt.Println("num变量的地址为:", p)
	fmt.Println("指针变量p的地址为:", &p)
	fmt.Println("指针变量p所指向的内容:", *p)
}

运行结果

num变量的地址为: 0x1400009c018
指针变量p的地址为: 0x140000a2018
指针变量p所指向的内容: 1
  • 注意:p指针声明后其值为nil,这时如果获取指针p指向的地址内容,则会出错!

编写代码🖋

package main

import "fmt"

func main() {
	var p *int
	fmt.Println("指针变量 p 指向的地址为:", p)
	fmt.Println("指针变量 p 所指向的内容:", *p)
}

运行结果

指针变量 p 指向的地址为: <nil>
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x2 addr=0x0 pc=0x100872cc8]

goroutine 1 [running]:
main.main()
        /Users/jingshaoxiang/MPM/test.go:136 +0x88
exit status 2

使用指针修改值

  • 在指针变量有实际指向的地址值后,可以通过如下格式直接修改指针所指向内存地址的内容: *变量名 = 修改值

编写代码🖋

package main

import "fmt"

func main() {
	num := 1
	var p *int
	p = &num
	fmt.Println("指针变量 p 所指的内容", *p)
	*p = 10
	fmt.Println("指针变量 p 所指的内容", *p)
}

运行结果

指针变量 p 所指的内容 1
指针变量 p 所指的内容 10
  • 在使用指针修改值时也需注意,可使用new()函数来给指针分配地址并初始化地址对应的值。

编写代码🖋

package main

import "fmt"

func main() {
	var p *int
	p = new(int)
	fmt.Println("指针变量 p 所指的内容", *p)
	*p = 10
	fmt.Println("指针变量 p 所指的内容", *p)
}

运行结果

指针变量 p 所指的内容 0
指针变量 p 所指的内容 10

其他数据类型

Go语言中有丰富的数据类型,本章主要介绍基本的数据类型,如整型、浮点型、字符串、字符和布尔型。除了以上类型,Go语言还有切片、通道、map(映射)和函数等类型。

小结

  • 整型主要分为有符号整型和无符号整型两大类。

  • Go语言常见的数据类型之间能够互相进行类型转换,通过使用类型前置加小括号的方式进行。