Go语言基础—数据类型间的故事
目录
前言
Go语言是Google开发的一种静态强类型、编译型语言。Go语言语法与C相近,但功能上有:内存安全,GC(垃圾回收),结构形态及CSP-style并发计算。本文档将详细介绍Go语言的基本数据类型和运算符。
基本数据类型
Go语言数据类型架构图
Go语言数据类型体系
│
┌─────────────────┼─────────────────┐
│ │ │
基本数据类型 复合数据类型 其他类型
(Basic Types) (Composite Types) (Other Types)
│ │ │
┌───────────┼───────────┐ │ ┌───────────┼───────────┐
│ │ │ │ │ │ │
数值类型 布尔类型 字符串类型 │ 指针类型 函数类型 接口类型
(Numeric) (Boolean) (String) │ (Pointer) (Function) (Interface)
│ │ │ │ │ │ │
┌────┼────┐ │ │ │ *Type func() interface{}
│ │ │ │ │ │ │
整型 浮点型 复数型 │ │ │ ┌─────┼─────┐
(Integer)(Float)(Complex) │ │ │ │ │
│ │ │ │ │ │ 内置函数 用户函数 方法
│ │ │ │ │ │ │ │
│ │ │ bool string │ │ method
│ │ │ │ │
│ │ ┌─┼─┐ │ func name()
│ │ │ │ │
│ ┌─┼─┐│ │ │ ┌───────┼───────┐
│ │ ││ │ │ │ │ │
│float32││complex64 集合类型 结构化类型 通道类型
│float64││complex128 (Collection)(Structured)(Channel)
│ │└─┴─┘ │ │ │
│ │ │ │ chan T
│ └─ math.Pi (常量) │ │ chan<- T
│ │ │ <-chan T
│ │ │
┌──┼──────────────┐ │ ┌──┼──┐
│ │ │ │ │ │
有符号整型 无符号整型 │ 结构体 自定义类型
(Signed) (Unsigned) │ (Struct)(Custom)
│ │ │ │ │
├─ int8 ├─ uint8(byte) │ struct{} type Name Type
├─ int16 ├─ uint16 │ │
├─ int32(rune) ├─ uint32 │ └─ 字段(Fields)
├─ int64 ├─ uint64 │ ├─ 导出字段(Exported)
└─ int ├─ uint │ └─ 未导出字段(Unexported)
(平台相关) └─ uintptr │
(指针大小) │
│
┌────────┼────────┐
│ │ │
数组 切片 映射
(Array) (Slice) (Map)
│ │ │
[n]Type []Type map[K]V
│ │ │
├─ 固定长度 ├─ 动态长度 ├─ 键值对
├─ 值类型 ├─ 引用类型 ├─ 引用类型
└─ 内存连续 ├─ 底层数组 └─ 哈希表
├─ 长度(len)
├─ 容量(cap)
└─ 指针指向数组
特殊字符类型:
├─ byte = uint8 (ASCII字符, 1字节)
└─ rune = int32 (Unicode字符, 4字节, UTF-8编码)
内存大小对照表:
┌─────────────┬─────────────┬─────────────┐
│ 类型 │ 大小(字节) │ 范围 │
├─────────────┼─────────────┼─────────────┤
│ bool │ 1 │ true/false │
│ int8 │ 1 │ -128~127 │
│ uint8 │ 1 │ 0~255 │
│ int16 │ 2 │ -32768~32767│
│ uint16 │ 2 │ 0~65535 │
│ int32 │ 4 │ ±21亿 │
│ uint32 │ 4 │ 0~42亿 │
│ int64 │ 8 │ ±922万亿 │
│ uint64 │ 8 │ 0~1844万亿 │
│ float32 │ 4 │ ±3.4e38 │
│ float64 │ 8 │ ±1.8e308 │
│ complex64 │ 8 │ float32实部虚部│
│ complex128 │ 16 │ float64实部虚部│
│ string │ 可变 │ UTF-8字符串 │
│ uintptr │ 平台相关 │ 存储指针 │
└─────────────┴─────────────┴─────────────┘
类型零值表:
├─ 数值类型: 0
├─ 布尔类型: false
├─ 字符串类型: ""
├─ 指针、切片、映射、通道、函数、接口: nil
└─ 数组、结构体: 所有字段为对应类型的零值
整形
整型分为以下两个大类:
- 有符号整形按长度分为:
int8
、int16
、int32
、int64
- 对应的无符号整型:
uint8
、uint16
、uint32
、uint64
基本示例
package main
import "fmt"
func main() {
var num int64
num = 123
fmt.Printf("num=%v 类型是%T", num, num)
}
输出:
num=123 类型是int64
字节
字节也叫 Byte,是计算机数据的基本存储单位。
单位换算:
- 8bit(位) = 1Byte(字节)
- 1024Byte(字节) = 1KB
- 1024KB = 1MB
- 1024MB = 1GB
- 1024GB = 1TB
注意: 在电脑里一个中文字是占三个字节的(UTF-8编码)。
特殊整形
Go语言中有一些具有特定用途的整数类型:
类型 | 描述 |
---|---|
uint |
32位操作系统上就是uint32,64位操作系统上就是uint64 |
int |
32位操作系统上就是int32,64位操作系统上就是int64 |
uintptr |
无符号整型,用于存放一个指针 |
注意事项
- 在使用
int
和uint
类型时,不能假定它是32位或64位的整型,而是考虑int
和uint
可能在不同平台上的差异。 - 实际项目中整数类型、切片、map 的元素数量等都可以用
int
来表示。 - 在涉及到二进制传输、为了保持文件的结构不会受到不同编译目标平台字节长度的影响,不要使用
int
和uint
。
unsafe.Sizeof
unsafe.Sizeof(n1)
是 unsafe
包的一个函数,可以返回 n1
变量占用的字节数。
package main
import (
"fmt"
"unsafe"
)
func main() {
var num int64
num = 123
fmt.Printf("num=%v 类型是%T\n", num, num)
fmt.Println(unsafe.Sizeof(num))
}
输出:
num=123 类型是int64
8
数字字面量语法
Go1.13版本之后引入了数字字面量语法,这样便于开发者以二进制、八进制或者十六进制浮点数的格式定义数字。
示例:
v := 0b101101
代表二进制的101101,相当于45v := 0o377
代表八进制的377,相当于255v := 0x1p-2
代表十六进制的1除以2²,也就是0.25v := 123_456
等于123456(可以用下划线分割数字)
package main
import "fmt"
func main() {
// 十进制
var a int = 10
fmt.Printf("%d\n", a) // 输出十进制
fmt.Printf("%b\n", a) // 输出二进制
// 八进制,以0开头
var b int = 077
fmt.Printf("%o\n", b) // 输出八进制
// 十六进制,以0x开头
var c int = 0xff
fmt.Printf("%x\n", c) // 输出十六进制
}
输出:
10
1010
77
ff
浮点型
Go语言支持两种浮点型数:float32
和 float64
。
float32
的浮点数的最大范围约为 3.4e38,可以使用常量math.MaxFloat32
float64
的浮点数的最大范围约为 1.8e308,可以使用常量math.MaxFloat64
打印浮点数时,可以使用 fmt
包配合动词 %f
。
package main
import (
"fmt"
"math"
)
func main() {
fmt.Printf("%f\n", math.Pi) // 输出:3.141593
fmt.Printf("%.2f\n", math.Pi) // 输出:3.14
}
Go语言中浮点数默认是float64
package main
import "fmt"
func main() {
a := 1.2
fmt.Printf("a=%v\t类型是:%T", a, a)
}
输出:
a=1.2 类型是:float64
Golang中float精度丢失问题
几乎所有的编程语言都有精度丢失这个问题,这是典型的二进制浮点数精度损失问题,在定长条件下,二进制小数和十进制小数互转可能有精度丢失。
package main
import "fmt"
func main() {
a := 1.2
fmt.Println(a * 100) // 输出:120
var d float64 = 1129.6
fmt.Println(d * 100) // 输出:112959.99999999999
}
布尔值
Go语言以 bool
类型进行声明布尔型数据,布尔型数据只有 true
和 false
两个值。
注意事项
- 布尔类型变量的默认值为
false
- Go语言中不允许将整形强制转换为布尔型
- 布尔型无法参与数值运算,也无法与其他类型进行转换
package main
import (
"fmt"
"unsafe"
)
func main() {
var b = true
fmt.Println(b, "占用字节:", unsafe.Sizeof(b))
}
字符串
Go语言中的字符串以原生数据类型出现,使用字符串就像使用其他原生数据类型(int、bool、float32、float64等)一样。Go语言中的字符串的内部实现使用UTF-8编码。字符串的值为双引号中的内容,可以在Go语言的源码中直接添加非ASCII码字符。
字符串转义符
Go语言的字符串常见转义符:
转义符 | 含义 |
---|---|
\r |
回车符(返回行首) |
\n |
换行符(直接跳到下一行的同列位置) |
\t |
制表符 |
\' |
单引号 |
\" |
双引号 |
\\ |
反斜杠 |
常用字符串操作
1. 拼接字符串
package main
import "fmt"
func main() {
var str1 = "你好"
var str2 = "golang"
// 方法1:使用 + 操作符
fmt.Println(str1 + str2)
// 方法2:使用 fmt.Sprintf
var str3 = fmt.Sprintf("%v%v", str1, str2)
fmt.Println(str3)
}
输出:
你好golang
你好golang
2. 分割字符串
package main
import (
"fmt"
"strings"
)
func main() {
var str = "golang"
var result = strings.Split(str, "lan")
fmt.Println(result) // 输出:[go g]
}
3. 判断字符串首尾字符
package main
import (
"fmt"
"strings"
)
func main() {
var str1 = "golang"
var hasPrefix = strings.HasPrefix(str1, "g")
fmt.Println(hasPrefix) // 输出:true
var str2 = "this is golang"
var hasSuffix = strings.HasSuffix(str2, "is")
fmt.Println(hasSuffix) // 输出:false
}
4. 判断字符串出现的位置
package main
import (
"fmt"
"strings"
)
func main() {
var str1 = "golang"
var index = strings.Index(str1, "g")
fmt.Println(index) // 输出:0
var str2 = "this is golang"
var lastIndex = strings.LastIndex(str2, "is")
fmt.Println(lastIndex) // 输出:5
}
5. Join拼接字符串
package main
import (
"fmt"
"strings"
)
func main() {
str := "123-456-789"
arr := strings.Split(str, "-") // 分割后得到 ["123", "456", "789"]
str2 := strings.Join(arr, "*") // 拼接后得到 "123*456*789"
fmt.Println(str2) // 输出:123*456*789
}
byte和rune类型
组成每个字符串的元素叫做"字符",可以通过遍历字符串元素获得字符。字符用单引号包裹。
基本概念
- 字节(byte):是计算机中数据处理的基本单位,习惯上用大写B来表示。1B(byte字节) = 8bit(位)
- 字符:是指计算机中使用的字母、数字、符号
- 重要提示:一个汉字占用3个字节,一个字母占用一个字节
package main
import "fmt"
func main() {
a := 'a'
b := '0'
// 当我们直接输出byte(字符)的时候输出的是这个字符对应的码值
fmt.Println(a) // 输出:97
fmt.Println(b) // 输出:48
// 如果我们要输出这个字符,需要格式化输出
fmt.Printf("%c--%c\n", a, b) // 输出:a--0
c := "m"
fmt.Println(len(c)) // 输出:1
d := "张"
fmt.Println(len(d)) // 输出:3
}
Go语言的字符类型
Go语言的字符有以下两种类型:
- uint8类型,或者叫
byte
型,代表了ASCII码的一个字符 - rune类型,代表一个UTF-8字符
当需要处理中文、日文或者其他复合字符时,则需要用到 rune
类型。rune
类型实际是一个 int32
。
// 遍历字符串
package main
import "fmt"
func main() {
s := "hello 张三"
// 按字节遍历
for i := 0; i < len(s); i++ {
fmt.Printf("%v(%c) ", s[i], s[i])
}
fmt.Println()
// 按rune遍历
for _, r := range s {
fmt.Printf("%v(%c) ", r, r)
}
fmt.Println()
}
字符串和字符的区别
c3 := "营" // 字符串类型
c4 := '营' // rune类型(int32)
fmt.Printf("C3的类型%T--C4的类型%T", c3, c4) // 输出:C3的类型string--C4的类型int32
修改字符串
要修改字符串,需要先将其转换成 []rune
或 []byte
,完成后再转换为 string
。无论哪种转换,都会重新分配内存,并复制字节数组。
func changeString() {
s1 := "big"
// 强制类型转换
byteS1 := []byte(s1)
byteS1[0] = 'p'
fmt.Println(string(byteS1)) // 输出:pig
s2 := "白萝卜"
runeS2 := []rune(s2)
runeS2[0] = '红'
fmt.Println(string(runeS2)) // 输出:红萝卜
}
运算符
算术运算符
运算符 | 描述 | 实例 |
---|---|---|
+ |
相加 | A + B |
- |
相减 | A - B |
* |
相乘 | A * B |
/ |
相除 | B / A |
% |
求余 | B % A |
注意事项
++
(自增)和 --
(自减)在Go语言中是单独的语句,并不是运算符。
var i int = 1
i++
fmt.Println("i=", i) // 输出:i= 2
关系运算符
运算符 | 描述 | 实例 |
---|---|---|
== |
检查两个值是否相等,如果相等返回 True 否则返回 False | A == B |
!= |
检查两个值是否不相等,如果不相等返回 True 否则返回 False | A != B |
> |
检查左边值是否大于右边值,如果是返回 True 否则返回 False | A > B |
< |
检查左边值是否小于右边值,如果是返回 True 否则返回 False | A < B |
>= |
检查左边值是否大于等于右边值,如果是返回 True 否则返回 False | A >= B |
<= |
检查左边值是否小于等于右边值,如果是返回 True 否则返回 False | A <= B |
逻辑运算符
运算符 | 描述 | 实例 |
---|---|---|
&& |
逻辑 AND 运算符。如果两边的操作数都是 True,则条件 True,否则为 False | A && B |
|| |
逻辑 OR 运算符。如果两边的操作数有一个 True,则条件 True,否则为 False | A || B |
! |
逻辑 NOT 运算符。如果条件为 True,则逻辑 NOT 条件 False,否则为 True | !A |
位运算符
位运算符对整数在内存中的二进制位进行操作。
运算符 | 描述 | 实例 |
---|---|---|
& |
按位与运算符"&"是双目运算符。其功能是参与运算的两数各对应的二进位相与 | A & B |
| |
按位或运算符"|"是双目运算符。其功能是参与运算的两数各对应的二进位相或 | A | B |
^ |
按位异或运算符"^"是双目运算符。其功能是参与运算的两数各对应的二进位相异或,当二进位不同时,结果为1 | A ^ B |
<< |
左移运算符"<<"是双目运算符。左移n位就是乘以2的n次方 | A << 2 |
>> |
右移运算符">>"是双目运算符。右移n位就是除以2的n次方 | A >> 2 |
位运算示例
package main
import "fmt"
func main() {
/*
& 两位均为 1 才为 1
| 两位有一个为 1 就为 1
^ 相异或,两位不一样则为 1
<< 左移 n 位就是乘以 2 的 n 次方。
"a<<b"是把 a 的各二进位全部左移 b 位,高位丢弃,低位补 0
>> 右移 n 位就是除以 2 的 n 次方
*/
var a int = 5 // 二进制:101
var b int = 2 // 二进制:010
fmt.Println("a&b =", a&b) // 000,结果为 0
fmt.Println("a|b =", a|b) // 111,结果为 7
fmt.Println("a^b =", a^b) // 111,结果为 7
fmt.Println("5>>2 =", a>>b) // 5 右移 2 位,结果为 1
fmt.Println("5<<2 =", a<<b) // 5 左移 2 位,结果为 20(二进制:10100)
fmt.Println("5<<1 =", 5<<1) // 5 左移 1 位,结果为 10(二进制:1010)
fmt.Println("5>>1 =", 5>>1) // 5 右移 1 位,结果为 2(二进制:10)
fmt.Println("7>>2 =", 7>>2) // 7 右移 2 位,结果为 1
}
赋值运算符
运算符 | 描述 | 实例 |
---|---|---|
= |
简单的赋值运算符,将一个表达式的值赋给一个左值 | C = A + B 将 A + B 表达式结果赋值给 C |
+= |
相加后再赋值 | C += A 等于 C = C + A |
-= |
相减后再赋值 | C -= A 等于 C = C - A |
*= |
相乘后再赋值 | C *= A 等于 C = C * A |
/= |
相除后再赋值 | C /= A 等于 C = C / A |
%= |
求余后再赋值 | C %= A 等于 C = C % A |
<<= |
左移后赋值 | C <<= 2 等于 C = C << 2 |
>>= |
右移后赋值 | C >>= 2 等于 C = C >> 2 |
&= |
按位与后赋值 | C &= 2 等于 C = C & 2 |
^= |
按位异或后赋值 | C ^= 2 等于 C = C ^ 2 |
|= |
按位或后赋值 | C |= 2 等于 C = C | 2 |
总结
本文档详细介绍了Go语言的基本数据类型和运算符:
- 基本数据类型包括整型、浮点型、布尔型、字符串、字符和字节类型
- 运算符包括算术、关系、逻辑、位运算和赋值运算符
- 重点概念包括字符串操作、字符编码处理、类型转换等
掌握这些基础知识是学习Go语言的重要基础,为后续学习更高级的Go语言特性打下坚实的基础。