数据类型
基本类型
- bool
- string
- int、int8、int16、int32、int64
- uint、uint8、uint16、uint32、uint64、uintptr
- byte (uint8 的别名)、rune (int32 的别名)
- float32、float64
-
值类型
-
引用类型
-
接口类型
-
函数类型
func
整数型 int
介绍
- Go语言同时提供了有符号和无符号的整数类型,内存对应 数字(bit)/8 大小的字节 :
- 有符号:int8、 int16、 int32 、 int64
- 无符号:uint8、uint16、uint32 、uint64
- 还有两种整数类型,它们分别对应特定 CPU 平台的字长(机器字大小),实际开发中由于编译器和计算机硬件的不同,他们所能表示的大小会在 32bit 或 64bit 之间变化:
- 有符号:int
- 无符号:uint
- uintptr 是无符号的整数类型,它没有指定具体的 bit 大小但是足以容纳指针。该类型只有在底层编程时才需要,特别是Go语言和C语言函数库或操作系统接口相交互的地方。
- rune 和 int32 是等价的,rune 通常用于表示一个 Unicode 码点。这两个名称可以互换使用。
- byte 和 uint8 是等价的,byte 通常用于强调数值是一个原始的数据而不是一个小的整数。
注意
- 大多数情况下,我们只需要 int 一种整型即可,它可以用于循环计数器(for 循环中控制循环次数的变量)、数组和切片的索引,以及任何通用目的的整型运算符,通常 int 类型的处理速度也是最快的。
- 尽管在某些特定的运行环境下 int、uint 和 uintptr 的大小可能相等,但是它们依然是不同的类型,比如 int 和 int32,虽然 int 类型的大小也可能是 32 bit,但是在需要把 int 类型当做 int32 类型使用的时候必须显示的对类型进行转换,反之亦然。
- Go语言中有符号整数采用 2 的补码形式表示,也就是最高 bit 位用来表示符号位,一个 n-bit 的有符号数的取值范围是从 -2 到 2-1。无符号整数的所有 bit 位都用于表示非负数,取值范围是 0 到 2-1。例如,int8 类型整数的取值范围是从 -128 到 127,而 uint8 类型整数的取值范围是从 0 到 255。可以从math包,查询,例:
math.MaxInt32
。
哪些情况下使用 int 和 uint
- 程序逻辑对整型范围没有特殊需求。例如,对象的长度使用内建 len() 函数返回,这个长度可以根据不同平台的字节长度进行变化。实际使用中,切片或 map 的元素数量等都可以用 int 来表示。
- 在二进制传输、读写文件的结构描述时,为了保持文件的结构不会受到不同编译目标平台字节长度的影响,不要使用 int 和 uint。
浮点型 float
介绍
- Go语言提供了两种精度的浮点数 float32 和 float64,它们的算术规范由 IEEE754 浮点数国际标准定义,该浮点数规范被所有现代的 CPU 支持。
- 浮点数类型的取值范围可以从很微小到很巨大,极限值可以在 math 包中找到:
- float32:最小值:1.4e-45 最大值:常量 math.MaxFloat32,大约是 3.4e38,内存占用4字节。
- float64:最小值:4.9e-324 最大值:常量 math.MaxFloat64,大约是 1.8e308,内存占用8字节。
- 小数位精度不同:
- float32:6 个十进制数的精度
- float64:15 个十进制数的精度
注意
通常应该优先使用 float64 类型,因为 float32 类型的累计计算误差很容易扩散,并且 float32 能精确表示的正整数并不是很大。
var f float32 = 16777216 // 1 << 24
fmt.Println(f == f+1) // "true"!
浮点数在声明的时候可以只写整数部分或者小数部分,像下面这样:
const e = .71828 // 0.71828
const f = 1. // 1
很小或很大的数最好用科学计数法书写,通过 e 或 E 来指定指数部分:
const Avogadro = 6.02214129e23 // 阿伏伽德罗常数
const Planck = 6.62606957e-34 // 普朗克常数
用 Printf 函数打印浮点数时可以使用“%f”来控制保留几位小数:
package main
import (
"fmt"
"math"
)
func main() {
fmt.Printf("%f\n", math.Pi) // 3.141593
fmt.Printf("%.2f\n", math.Pi) // 3.14
}
复数型 complex
介绍
- 复数是由两个浮点数表示的,其中一个表示实部(real),一个表示虚部(imag)。
- 复数的值由三部分组成 RE + IMi,其中 RE 是实数部分,IM 是虚数部分,RE 和 IM 均为 float 类型。最后的 i 是虚数单位。
- Go语言中复数的类型有两种:
- complex64(32 位实数和虚数,占用8字节)
- complex128(64 位实数和虚数,占用16字节)
- Go语言内置的 math/cmplx 包中提供了很多操作复数的公共方法,Go语言默认使用 complex128 类型,因为这些内置的包中都使用 complex128 类型作为参数。
声明
- 内置函数 complex(x, y) 来声明复数,x 为实部,y 为虚部,皆为 float64。
- 内置函数 real(z) 来获得复数的实部。
- 内置函数 imag(z) 来获得复数的虚部。
var x complex128 = complex(1, 2) // 1+2i
var y complex128 = complex(3, 4) // 3+4i
fmt.Println(x*y) // "(-5+10i)"
fmt.Println(real(x*y)) // "-5"
fmt.Println(imag(x*y)) // "10"
如果大家对复数的运算法则不是很了解,可以查阅《复数运算法则》,其中详细的讲解了复数的加减乘除操作。
布尔型 bool
介绍
- 布尔类型的值只有两种:true 或 false。不可以用数字代替。
- if 和 for 语句的条件部分都是布尔类型的值,并且
==
和<
等比较操作也会产生布尔型的值。 - 一元操作符
!
对应逻辑非操作,因此!true
的值为 false。 - 在内存中占用1个字节。
注意
- Go语言对于值之间的比较有非常严格的限制,如果以下条件都不满足,则必须将其中一个值的类型转换为和另外一个值的类型相同之后才可以进行比较:
- 只有两个相同类型的值才可以进行比较。
- 如果值的类型是接口(interface),那么它们也必须都实现了相同的接口。
- 如果其中一个值是常量,那么另外一个值可以不是常量,但是类型必须和该常量类型相同。
布尔值可以和 &&(AND)和 ||(OR)操作符结合(&& 优先级比 || 高),并且有短路行为,如果运算符左边的值已经可以确定整个布尔表达式的值,那么运算符右边的值将不再被求值。
s != "" && s[0] == 'x'
布尔型无法参与数值运算,也无法与其他类型进行转换。
var n bool
fmt.Println(int(n) * 2)
会编译错误:cannot convert n (type bool) to type int
字符串 string
介绍
- 字符串可以包含任意数据,通常是用来包含可读的文本,使用””或``来定义。
- 字符串是一种值类型,且值不可变,即创建某个文本后将无法再次修改这个文本的内容,更深入地讲,字符串是字节的定长数组。
- 字符串是 UTF-8 字符的一个序列(当字符为 ASCII 码表上的字符时则占用 1 个字节,其它字符根据需要占用 2-4 个字节)。
UTF-8
单行字符串(字符串字面量 string literal)
使用双引号书写字符串的方式是字符串常见表达方式之一,双引号字面量不能跨行。
s := "hello word!"
多行字符串
使用反引号书写字符串的方式也是字符串常见表达方式之一,反引号字面量可以跨行。
- 反引号间换行将被作为字符串中的换行,但是所有的转义字符均无效,文本将会原样输出。
- 多行字符串一般用于内嵌源码和内嵌数据等,立马所有内容均不会被编译器识别,而只是作为字符串的一部分。
const str = `第一行
第二行
第三行
\r\n
`
fmt.Println(str)
转译
字符串中可以使用转义字符来实现换行、缩进等效果,常用的转义字符包括:
- \n:换行符
- \r:回车符
- \t:tab 键
- \u 或 \U:Unicode 字符
- \:反斜杠自身
注意
- 一般的比较运算符(==、!=、<、<=、>=、>)是通过在内存中按字节比较来实现字符串比较的,因此比较的结果是字符串自然编码的顺序。
- 字符串的内容(纯字节)可以通过标准索引法来获取,在方括号
[ ]
内写入索引,索引从 0 开始计数:
2. +=
s := "hel" + "lo,"
s += "world!"
fmt.Println(s) //输出 “hello, world!”
其他
字符串类型在业务中的应用可以说是最广泛的,读者需要详细了解字符串的常见用法,请猛击下面的文章:
- Go语言计算字符串长度——len()和RuneCountInString()
- Go语言遍历字符串——获取每一个字符串元素
- Go语言字符串截取(获取字符串的某一段字符)
- Go语言修改字符串
- Go语言字符串拼接(连接)
- Go语言fmt.Sprintf(格式化输出)
- Go语言Base64编码——电子邮件的基础编码格式
字符类型 byte 和 rune
介绍
- 字符串中的每一个元素叫做“字符”,在遍历或者单个获取字符串元素时可以叫做字符。字符使用单引号括起来。
Go语言的字符有以下两种:
ASCII 码表中,下面的写法是等效的:
var ch byte = 65
var ch byte = '\377' //(\ 后面紧跟着长度为 3 的 8 进制数)
var ch byte = '\x41' //(\x 后面紧跟着长度为 2 的 16 进制数)
rune
Go语言同样支持 Unicode(UTF-8),因此字符同样称为 Unicode 代码点或者 runes,并在内存中使用 int 来表示。在文档中,一般使用格式 U+hhhh 来表示,其中 h 表示一个 16 进制数。
- 在书写 Unicode 字符时,需要在 16 进制数之前加上前缀
\u
或者\U
。因为 Unicode 至少占用 2 个字节,所以我们使用 int16 或者 int 类型来表示。如果需要使用到 4 字节,则使用\u
前缀,如果需要使用到 8 个字节,则使用\U
前缀。 - Unicode 包中内置了一些用于测试字符的函数,这些函数的返回值都是一个布尔值:
- 判断是否为字母:unicode.IsLetter(c)
- 判断是否为数字:unicode.IsDigit(c)
- 判断是否为空白符号:unicode.IsSpace(c) ```go var ch int = ‘\u0041’ var ch2 int = ‘\u03B2’ var ch3 int = ‘\U00101234’ fmt.Printf(“%d - %d - %d\n”, ch, ch2, ch3) // integer fmt.Printf(“%c - %c - %c\n”, ch, ch2, ch3) // character fmt.Printf(“%X - %X - %X\n”, ch, ch2, ch3) // UTF-8 bytes fmt.Printf(“%U - %U - %U”, ch, ch2, ch3) // UTF-8 code point
// log 65 - 946 - 1053236 A - β - r 41 - 3B2 - 101234 U+0041 - U+03B2 - U+101234
<a name="oFBI5"></a>
##### UTF-8 和 Unicode 有何区别?
广义的 Unicode 指的是一个标准,定义字符集及编码规则,即 Unicode 字符集和 UTF-8、UTF-16 编码等。
<a name="Vawc7"></a>
###### Unicode
1. Unicode 与 ASCII 类似,都是一种字符集。
1. 字符集为每个字符分配一个唯一的 ID,我们使用到的所有字符在 Unicode 字符集中都有一个唯一的 ID,例如上面例子中的 a 在 Unicode 与 ASCII 中的编码都是 97。汉字“你”在 Unicode 中的编码为 20320,在不同国家的字符集中,字符所对应的 ID 也会不同。而无论任何情况下,Unicode 中的字符的 ID 都是不会变化的。
<a name="tGYJZ"></a>
###### UTF-8
1. UTF-8 是编码规则,将 Unicode 中字符的 ID 以某种方式进行编码,UTF-8 的是一种变长编码规则,从 1 到 4 个字节不等。编码规则如下:
1. 0xxxxxx 表示文字符号 0~127,兼容 ASCII 字符集。
1. 从 128 到 0x10ffff 表示其他字符。
2. 根据这个规则,拉丁文语系的字符编码一般情况下每个字符占用一个字节,而中文每个字符占用 3 个字节。
<br />
---
<a name="cdENJ"></a>
###
<a name="CivIt"></a>
## 类型转换
<a name="cQHRm"></a>
### 介绍
1. 在必要以及可行的情况下,一个类型的值可以被转换成另一种类型的值。由于Go语言不存在隐式类型转换,因此所有的类型转换都必须显式的声明。
1. 类型转换只能在定义正确的情况下转换成功,例如从一个取值范围较小的类型转换到一个取值范围较大的类型(将 int16 转换为 int32)。当从一个取值范围较大的类型转换到取值范围较小的类型时(将 int32 转换为 int16 或将 float32 转换为 int),会发生精度丢失(截断)的情况。
1. 只有相同底层类型的变量之间可以进行相互转换(如将 int16 类型转换成 int32 类型),不同底层类型的变量相互转换时会引发编译错误(如将 bool 类型转换为 int 类型)。
1. 浮点数在转换为整型时,会将小数部分去掉,只保留整数部分。
<a name="Vikke"></a>
### 使用
```go
valueOfTypeB = typeB(valueOfTypeA)
// demo
a := 5.0
b := int(a)
不同类型转换
To 系列
string转成int:
func Atoi(s string) (i int, err error)
int转成string:
func Itoa(i int) string
Parse 系列
- Parse 系列函数都有两个返回值,第一个返回值是转换后的值,第二个返回值为转化失败的错误信息。
- 返回的 err 是 *NumErr 类型的,如果语法有误,err.Error = ErrSyntax,如果结果超出类型范围 err.Error = ErrRange。 ```go string转成int64: // base 指定进制,取值范围是 2 到 36。如果 base 为 0,则会从字符串前置判断,“0x”是 16 进制,“0”是 8 进制,否则是 10 进制。 // bitSize 指定结果必须能无溢出赋值的整数类型,0、8、16、32、64 分别代表 int、int8、int16、int32、int64。 func ParseInt(s string, base int, bitSize int) (i int64, err error) // demo: i, err := strconv.ParseInt(“-10”, 10, 64)
string转成uint64: // base,bitSize 同 ParseInt() func ParseUint(s string, base int, bitSize int) (n uint64, err error) // demo: i, err := strconv.ParseInt(“10”, 10, 64)
string转成bool: // 只能接受 1、0、t、f、T、F、true、false、True、False、TRUE、FALSE,其它的值均返回错误 bool,err := strconv.ParseBool(string)
string转成float64: // bitSize 指定了返回值的类型,32 表示 float32,64 表示 float64。 func ParseFloat(s string, bitSize int) (f float64, err error)
<a name="QSAc8"></a>
#### Format 系列
Format 系列函数实现了将给定类型数据格式化为字符串类型的功能。
```go
int64转成string
// 参数 base 必须在 2 到 36 之间,返回结果中会使用小写字母“a”到“z”表示大于 10 的数字。
func FormatInt(i int64, base int) string
uint64转成string
// base 同 FormatInt()
func FormatUint(i uint64, base int) string
bool转成string
func FormatBool(b bool) string
float64转成string
// fmt 表示格式,可以设置为“f”表示 -ddd.dddd、“b”表示 -ddddp±ddd,指数为二进制、“e”表示 -d.dddde±dd 十进制指数、“E”表示 -d.ddddE±dd 十进制指数、“g”表示指数很大时用“e”格式,否则“f”格式、“G”表示指数很大时用“E”格式,否则“f”格式。
// prec 控制精度(排除指数部分):当参数 fmt 为“f”、“e”、“E”时,它表示小数点后的数字个数;当参数 fmt 为“g”、“G”时,它控制总的数字个数。如果 prec 为 -1,则代表使用最少数量的、但又必需的数字来表示 f。
// bitSize 表示参数 f 的来源类型(32 表示 float32、64 表示 float64),会据此进行舍入。
func FormatFloat(f float64, fmt byte, prec, bitSize int) string
append 系列
- Append 系列函数用于将指定类型转换成字符串后追加到一个切片中。
Append 系列函数和 Format 系列函数的使用方法类似,只不过是将转换后的结果追加到一个切片中。 ```go package main import ( “fmt” “strconv” ) func main() { // 声明一个slice b10 := []byte(“int (base 10):”)
// 将转换为10进制的string,追加到slice中 b10 = strconv.AppendInt(b10, -42, 10) fmt.Println(string(b10)) b16 := []byte(“int (base 16):”) b16 = strconv.AppendInt(b16, -42, 16) fmt.Println(string(b16)) }
// log int (base 10):-42 int (base 16):-2a ```