Go语言数据类型全解析
Go 语言提供了丰富的数据类型系统,可分为基本类型、复合类型、引用类型和接口类型四大类。这些数据类型的设计体现了 Go 语言简洁、高效和安全的特点,同时兼顾了性能与开发效率的平衡。
一、基本数据类型
1. 整数类型
Go 提供了多种整数类型以满足不同场景的需求,包括内存敏感型应用、系统编程和数值计算等场景:
有符号整数:
- int8:8 位有符号整数,范围 -128 到 127
- 适用场景:小范围数值存储,如温度传感器读数(-40~50)、小型计数器
- 内存占用:1字节
- int16:16 位有符号整数,范围 -32768 到 32767
- 适用场景:中等范围数值,如音频采样值、小型设备状态码
- 内存占用:2字节
- int32:32 位有符号整数,范围 -2147483648 到 2147483647
- 适用场景:一般整数运算,如计数器、时间戳(秒级)
- 内存占用:4字节
- int64:64 位有符号整数,范围 -9223372036854775808 到 9223372036854775807
- 适用场景:大整数运算,如金融计算、纳秒级时间戳
- 内存占用:8字节
- int:根据系统位数自动确定,32位系统上为32位,64位系统上为64位
- 推荐在大多数情况下使用,除非有明确的内存或范围限制
- 内存占用:4或8字节,取决于平台
无符号整数:
- uint8:8 位无符号整数,范围 0 到 255
- 常用于图像处理中的像素值、小型枚举类型
- 内存占用:1字节
- uint16:16 位无符号整数,范围 0 到 65535
- 适用于网络端口号、16位编码字符集
- 内存占用:2字节
- uint32:32 位无符号整数,范围 0 到 4294967295
- 用于IP地址存储、32位哈希值
- 内存占用:4字节
- uint64:64 位无符号整数,范围 0 到 18446744073709551615
- 大范围无符号数值计算、文件大小表示
- 内存占用:8字节
- uint:与系统位数相同
- 推荐在需要无符号整数时使用
- 内存占用:4或8字节,取决于平台
特殊整数类型:
- uintptr:无符号整数,足够大以存储指针值,主要用于底层编程
- 如CGO交互、内存地址操作等
- 内存占用:与平台指针大小相同
- byte:uint8 的别名,强调原始数据而非数值
- 用于二进制数据读写、原始字节处理
- 内存占用:1字节
- rune:int32 的别名,表示一个 Unicode 码点
- 用于处理UTF-8编码的字符、文本处理
- 内存占用:4字节
2. 浮点类型
Go 提供了两种精度的浮点数,满足不同精度要求的计算场景:
float32:
- 32 位浮点数,IEEE-754 标准
- 精度:约 6-7 位有效数字
- 范围:约 ±3.4e+38
- 优点:内存占用小(4字节)
- 缺点:精度有限,可能产生舍入误差
- 适用场景:
- 3D图形计算(如OpenGL)
- 嵌入式系统等内存受限环境
- 不需要高精度的科学计算
float64:
- 64 位浮点数,IEEE-754 标准(默认浮点类型)
- 精度:约 15-16 位有效数字
- 范围:约 ±1.7e+308
- 优点:精度高,减少舍入误差
- 缺点:内存占用大(8字节)
- 适用场景:
- 科学计算
- 金融计算
- 需要高精度的场景
- 大多数通用计算
示例:
var f1 float32 = 3.14 // 显式声明float32
f2 := 3.141592653589793 // 默认为 float64
f3 := float32(3.14) // 类型转换// 精度比较
fmt.Println(float32(1.0/3.0)) // 0.33333334
fmt.Println(float64(1.0/3.0)) // 0.3333333333333333// 科学计数法表示
f4 := 1.23e-4 // float64
f5 := float32(6.02e23) // float32
3. 复数类型
Go 直接支持复数运算,这在信号处理、工程计算等领域非常有用:
complex64:
- 由两个 float32 组成的复数
- 实部和虚部各占32位
- 内存占用:8字节
- 适用场景:对内存敏感但需要复数运算的情况
complex128:
- 由两个 float64 组成的复数(默认复数类型)
- 实部和虚部各占64位
- 内存占用:16字节
- 适用场景:需要高精度复数运算的情况
示例:
var c1 complex64 = complex(1, 2) // 1 + 2i
c2 := 3 + 4i // 类型为 complex128// 复数运算
fmt.Println(real(c2)) // 获取实部: 3
fmt.Println(imag(c2)) // 获取虚部: 4
fmt.Println(c1 + complex64(c2)) // 复数加法: (4+6i)// 复数乘法
c3 := complex(1, 1) * complex(1, -1) // (1+1i)*(1-1i) = 2+0i
fmt.Println(c3) // (2+0i)// 复数模
fmt.Println(cmplx.Abs(c2)) // 5 (3^2 + 4^2 = 25, sqrt(25)=5)
4. 布尔类型
bool 类型只有两个值:true 和 false。Go 严格区分布尔值和整数值,不能相互转换,这有助于避免常见错误。
特性:
- 零值为 false
- 不支持隐式转换,必须显式比较
- 占用1字节内存
- 不能与数值类型自动转换(与C语言不同)
- 在条件判断中必须使用布尔表达式
示例:
var b1 bool // 默认false
b2 := true// 条件判断
if b2 {fmt.Println("It's true")
}// 布尔运算
b3 := true && false // 与运算
b4 := true || false // 或运算
b5 := !true // 非运算// 错误示例
// var n int = 1
// if n { ... } // 编译错误:非布尔类型不能用作条件
// b6 := bool(1) // 编译错误:不能将数值转换为布尔// 正确方式
if n != 0 { // 显式比较fmt.Println("n is not zero")
}
5. 字符串类型
string 是不可变的 UTF-8 编码字节序列,设计用于高效处理Unicode文本:
特性:
- 底层是只读的字节切片
- 使用UTF-8编码,一个字符可能占用1-4个字节
- 长度通过 len() 获取,返回的是字节数而非字符数
- 不可变性保证了线程安全
- 零值为空字符串 ""
- 支持切片操作,但应谨慎处理UTF-8字符边界
创建方式:
- 使用反引号 ` 可以创建原始字符串(不转义)
- 适合正则表达式、多行文本、JSON模板等
- 可以包含换行符而不需要转义
- 使用双引号 " 创建可转义的字符串
- 支持转义字符如\n、\t、"等
- 适合大多数字符串场景
示例:
s1 := "Hello, 世界" // 可转义字符串
s2 := `Raw string \n will not escape` // 原始字符串
s3 := "Line 1\nLine 2" // 包含换行符// 字符串操作
fmt.Println(len("世界")) // 输出6,因为每个中文字符占3字节
fmt.Println(utf8.RuneCountInString("世界")) // 输出2,获取字符数// 字符串遍历(按字节)
for i := 0; i < len(s1); i++ {fmt.Printf("%x ", s1[i]) // 打印每个字节的十六进制
}// 字符串遍历(按字符)
for i, r := range "世界" {fmt.Printf("%d: %c\n", i, r)
}// 字符串拼接
s4 := "Hello" + ", " + "World"
s5 := strings.Join([]string{"Hello", "World"}, ", ")// 字符串切片(注意UTF-8字符边界)
s6 := s1[0:5] // "Hello"
s7 := s1[7:10] // "世"(前3个字节)
二、复合类型
1. 数组
数组是固定长度的相同类型元素的集合,长度是数组类型的一部分:
特点:
- 长度固定,编译时确定
- 值类型,赋值和传参会复制整个数组
- 内存布局紧凑,访问效率高
- 类型包含长度信息,[3]int和[5]int是不同的类型
- 零值为所有元素的零值
声明方式:
var arr1 [5]int // 初始化为零值 [0,0,0,0,0]
arr2 := [3]string{"a", "b", "c"} // 初始化赋值
arr3 := [...]int{1, 2, 3} // 编译器自动计算长度,等价于[3]int
arr4 := [5]int{1: 10, 3: 30} // 索引初始化 [0,10,0,30,0]
arr5 := [2][3]int{{1,2,3}, {4,5,6}} // 二维数组
数组操作:
// 访问和修改
fmt.Println(arr2[1]) // "b"
arr2[1] = "B"// 遍历
for i, v := range arr3 {fmt.Printf("%d: %d\n", i, v)
}// 数组比较(只有相同类型的数组才能比较)
arr6 := [3]int{1,2,3}
fmt.Println(arr3 == arr6) // true// 数组长度
fmt.Println(len(arr1)) // 5
fmt.Println(cap(arr1)) // 5 (数组的cap总是等于len)// 多维数组
matrix := [2][3]int{{1, 2, 3},{4, 5, 6},
}
fmt.Println(matrix[1][2]) // 6
2. 结构体
结构体是由不同类型字段组成的集合,用于创建自定义的复合数据类型:
定义和使用:
type Person struct {Name stringAge intAddress struct {City stringZip string}
}// 初始化方式1:字段名指定
p1 := Person{Name: "Alice",Age: 30,Address: struct {City stringZip string}{City: "Beijing",Zip: "100000",},
}// 初始化方式2:顺序指定(不推荐)
p2 := Person{"Bob", 25, struct{City string; Zip string}{"Shanghai", "200000"}}// 匿名结构体
p3 := struct {Name stringAge int
}{"Charlie", 35}
特性:
- 支持匿名字段(嵌入字段),常用于组合模式
- 字段可以包含标签(Tag),用于反射等场景
- 值类型,赋值会复制整个结构体
- 支持指针访问,使用->操作符自动解引用
- 字段可以导出(大写字母开头)或不可导出(小写字母开头)
示例:
// 匿名字段示例
type Circle struct {struct {X, Y int}Radius int
}c := Circle{struct{X, Y int}{0, 0}, 5}
fmt.Println(c.X, c.Y, c.Radius) // 可以直接访问嵌入字段// 结构体指针
pp := &Person{Name: "Dave"}
fmt.Println(pp.Name) // 自动解引用// 结构体标签
type User struct {ID int `json:"id" db:"user_id"`Name string `json:"name" db:"user_name"`
}// 结构体比较(只有所有字段可比较时才能比较)
p4 := Person{Name: "Alice", Age: 30}
p5 := Person{Name: "Alice", Age: 30}
fmt.Println(p4 == p5) // true
三、引用类型
1. 切片(Slice)
切片是动态数组的抽象,是Go中最常用的数据结构之一:
组成结构:
- 指向底层数组的指针
- 长度(len):当前元素个数
- 容量(cap):底层数组的总大小
创建方式:
var s1 []int // nil 切片,len=0, cap=0
s2 := []int{1, 2, 3} // 字面量初始化,len=cap=3
s3 := make([]int, 5, 10) // 长度5,容量10
s4 := arr[1:3] // 从数组创建,len=2, cap=4(取决于原数组)
s5 := make([]int, 0, 5) // 空切片,len=0, cap=5
常用操作:
// 添加元素(可能触发扩容)
s2 = append(s2, 4, 5) // [1,2,3,4,5]// 切片操作
s6 := s2[1:3] // [2,3]
s7 := s2[:4] // [1,2,3,4]
s8 := s2[2:] // [3,4,5]// 复制切片
s9 := make([]int, len(s2))
copy(s9, s2)// 扩容机制
for i := 0; i < 10; i++ {fmt.Printf("len=%d cap=%d\n", len(s2), cap(s2))s2 = append(s2, i)
}// 多维切片
matrix := [][]int{{1, 2, 3},{4, 5, 6},
}
fmt.Println(matrix[1][2]) // 6
2. 映射(Map)
映射是键值对集合,基于哈希表实现:
创建和初始化:
var m1 map[string]int // nil map,不能直接使用
m2 := make(map[string]int) // 空map
m3 := map[string]int{"one": 1,"two": 2,
}
m4 := make(map[string]int, 100) // 预分配空间
操作:
// 添加/修改
m3["three"] = 3// 获取
val := m3["one"] // 不存在返回零值
val, ok := m3["four"] // ok判断是否存在// 删除
delete(m3, "two")// 遍历
for k, v := range m3 {fmt.Printf("%s: %d\n", k, v)
}// 长度
fmt.Println(len(m3)) // 2// 复杂值类型
m5 := map[string][]int{"odds": {1, 3, 5},"evens": {2, 4, 6},
}
m5["primes"] = []int{2, 3, 5, 7}
3. 通道(Channel)
通道用于 goroutine 间通信,是Go并发模型的核心:
创建:
ch1 := make(chan int) // 无缓冲通道
ch2 := make(chan string, 5) // 缓冲容量为5
var ch3 chan float64 // nil channel
操作:
// 发送
ch1 <- 42// 接收
val := <-ch1// 关闭
close(ch1)// 多路选择
select {
case v := <-ch1:fmt.Println("received", v)
case ch2 <- "hello":fmt.Println("sent hello")
default:fmt.Println("no activity")
}// 通道遍历
for v := range ch2 {fmt.Println(v)
}// 单向通道
var sendOnly chan<- int = ch1
var recvOnly <-chan int = ch1
通道类型特性:
- 无缓冲通道:同步操作,发送和接收会阻塞直到另一端准备好
- 有缓冲通道:异步操作,发送不会阻塞直到缓冲区满,接收不会阻塞直到缓冲区空
- nil通道:发送和接收都会永久阻塞
- 已关闭通道:
- 接收操作会立即返回零值
- 发送操作会引发panic
- 关闭已关闭的通道会引发panic