Go 语言数据类型
Go 语言是静态类型语言,所有变量都有确定的类型。本章将详细介绍 Go 语言的类型系统,包括基本类型、复合类型和自定义类型。
🎯 类型系统概览
Go 语言类型分类
Go 数据类型
├── 基本类型 (Basic Types)
│ ├── 数值类型
│ │ ├── 整数类型 (int, int8, int16, int32, int64)
│ │ ├── 无符号整数 (uint, uint8, uint16, uint32, uint64)
│ │ ├── 浮点数 (float32, float64)
│ │ └── 复数 (complex64, complex128)
│ ├── 字符串类型 (string)
│ └── 布尔类型 (bool)
├── 复合类型 (Composite Types)
│ ├── 数组 (Array)
│ ├── 切片 (Slice)
│ ├── 映射 (Map)
│ ├── 结构体 (Struct)
│ ├── 指针 (Pointer)
│ ├── 函数 (Function)
│ ├── 接口 (Interface)
│ └── 通道 (Channel)
└── 自定义类型 (Custom Types)
└── 类型别名和类型定义🔢 数值类型
整数类型
有符号整数
go
package main
import (
"fmt"
"unsafe"
)
func main() {
// 有符号整数类型
var i8 int8 = 127 // 8位:-128 到 127
var i16 int16 = 32767 // 16位:-32768 到 32767
var i32 int32 = 2147483647 // 32位:-2147483648 到 2147483647
var i64 int64 = 9223372036854775807 // 64位:更大范围
// int 类型大小取决于平台(32位或64位)
var i int = 42
fmt.Printf("int8: %d, 大小: %d 字节\n", i8, unsafe.Sizeof(i8))
fmt.Printf("int16: %d, 大小: %d 字节\n", i16, unsafe.Sizeof(i16))
fmt.Printf("int32: %d, 大小: %d 字节\n", i32, unsafe.Sizeof(i32))
fmt.Printf("int64: %d, 大小: %d 字节\n", i64, unsafe.Sizeof(i64))
fmt.Printf("int: %d, 大小: %d 字节\n", i, unsafe.Sizeof(i))
}无符号整数
go
func main() {
// 无符号整数类型(只能表示非负数)
var ui8 uint8 = 255 // 8位:0 到 255
var ui16 uint16 = 65535 // 16位:0 到 65535
var ui32 uint32 = 4294967295 // 32位:0 到 4294967295
var ui64 uint64 = 18446744073709551615 // 64位:更大范围
// uint 类型大小取决于平台
var ui uint = 42
// 特殊类型
var b byte = 255 // byte 等同于 uint8
var r rune = '中' // rune 等同于 int32,用于表示Unicode字符
var ptr uintptr = 0x1000 // uintptr 用于存储指针地址
fmt.Printf("uint8: %d\n", ui8)
fmt.Printf("byte: %d\n", b)
fmt.Printf("rune: %d (%c)\n", r, r)
fmt.Printf("uintptr: 0x%x\n", ptr)
}整数字面量
go
func main() {
// 不同进制的整数字面量
var dec = 42 // 十进制
var bin = 0b101010 // 二进制(Go 1.13+)
var oct = 0o52 // 八进制
var hex = 0x2A // 十六进制
// 使用下划线提高可读性
var million = 1_000_000
var binary = 0b1010_1010_1010_1010
fmt.Printf("十进制: %d\n", dec)
fmt.Printf("二进制: %d\n", bin)
fmt.Printf("八进制: %d\n", oct)
fmt.Printf("十六进制: %d\n", hex)
fmt.Printf("百万: %d\n", million)
}浮点数类型
基本浮点数
go
import "math"
func main() {
// 浮点数类型
var f32 float32 = 3.14159
var f64 float64 = 3.141592653589793
// 默认浮点数类型是 float64
var pi = 3.14159 // 类型为 float64
fmt.Printf("float32: %.5f, 大小: %d 字节\n", f32, unsafe.Sizeof(f32))
fmt.Printf("float64: %.15f, 大小: %d 字节\n", f64, unsafe.Sizeof(f64))
// 特殊浮点数值
fmt.Printf("正无穷: %f\n", math.Inf(1))
fmt.Printf("负无穷: %f\n", math.Inf(-1))
fmt.Printf("NaN: %f\n", math.NaN())
// 浮点数精度问题
fmt.Printf("0.1 + 0.2 = %.17f\n", 0.1+0.2)
}浮点数字面量
go
func main() {
// 不同形式的浮点数字面量
var f1 = 3.14 // 小数形式
var f2 = .25 // 省略整数部分
var f3 = 1. // 省略小数部分
var f4 = 1e6 // 科学计数法:1 * 10^6
var f5 = 2.5e-3 // 科学计数法:2.5 * 10^-3
var f6 = 0x1.Fp+0 // 十六进制浮点数
fmt.Printf("f1: %g\n", f1)
fmt.Printf("f2: %g\n", f2)
fmt.Printf("f3: %g\n", f3)
fmt.Printf("f4: %g\n", f4)
fmt.Printf("f5: %g\n", f5)
fmt.Printf("f6: %g\n", f6)
}复数类型
go
func main() {
// 复数类型
var c64 complex64 = 1 + 2i // 实部和虚部都是 float32
var c128 complex128 = 3 + 4i // 实部和虚部都是 float64
// 使用 complex 函数创建复数
var c1 = complex(1.5, 2.5) // complex128
var c2 = complex(float32(1), float32(2)) // complex64
// 获取实部和虚部
fmt.Printf("c128: %v\n", c128)
fmt.Printf("实部: %g, 虚部: %g\n", real(c128), imag(c128))
// 复数运算
fmt.Printf("c1 + c2: %v\n", c1+complex128(c2))
fmt.Printf("c1 * c2: %v\n", c1*complex128(c2))
}📝 字符串类型
字符串基础
go
func main() {
// 字符串声明
var str1 string = "Hello, 世界!"
var str2 = "Go 语言"
str3 := "简洁高效"
// 字符串是不可变的
fmt.Printf("字符串: %s\n", str1)
fmt.Printf("长度: %d 字节\n", len(str1)) // 注意:len 返回字节数,不是字符数
// 字符串连接
result := str2 + " " + str3
fmt.Printf("连接结果: %s\n", result)
// 原始字符串(反引号)
rawStr := `这是原始字符串
可以包含换行符
和特殊字符 \n \t`
fmt.Printf("原始字符串:\n%s\n", rawStr)
}字符串操作
go
import (
"fmt"
"strings"
"unicode/utf8"
)
func main() {
str := "Hello, 世界!"
// 字节长度 vs 字符长度
fmt.Printf("字节长度: %d\n", len(str))
fmt.Printf("字符长度: %d\n", utf8.RuneCountInString(str))
// 字符串遍历
fmt.Println("按字节遍历:")
for i := 0; i < len(str); i++ {
fmt.Printf("%d: %c\n", i, str[i])
}
fmt.Println("按字符遍历:")
for i, r := range str {
fmt.Printf("%d: %c\n", i, r)
}
// 字符串切片
fmt.Printf("前5字节: %s\n", str[:5])
fmt.Printf("从第7字节开始: %s\n", str[7:])
// 字符串包含
fmt.Printf("包含'世界': %t\n", strings.Contains(str, "世界"))
fmt.Printf("前缀'Hello': %t\n", strings.HasPrefix(str, "Hello"))
}字符类型(rune)
go
func main() {
// rune 类型表示一个 Unicode 字符
var r1 rune = 'A' // ASCII 字符
var r2 rune = '中' // 中文字符
var r3 rune = '🚀' // Emoji 字符
// Unicode 转义
var r4 rune = '\u4e2d' // Unicode: 中
var r5 rune = '\U0001F680' // Unicode: 🚀
fmt.Printf("r1: %c (值: %d)\n", r1, r1)
fmt.Printf("r2: %c (值: %d)\n", r2, r2)
fmt.Printf("r3: %c (值: %d)\n", r3, r3)
fmt.Printf("r4: %c (值: %d)\n", r4, r4)
fmt.Printf("r5: %c (值: %d)\n", r5, r5)
// 字符串转 rune 切片
str := "Hello, 世界!"
runes := []rune(str)
fmt.Printf("字符数组: %v\n", runes)
fmt.Printf("第8个字符: %c\n", runes[7])
}✅ 布尔类型
go
func main() {
// 布尔类型只有两个值:true 和 false
var b1 bool = true
var b2 bool = false
var b3 = true // 类型推断
b4 := false // 短变量声明
// 布尔类型的零值是 false
var b5 bool
fmt.Printf("布尔零值: %t\n", b5)
// 布尔运算
fmt.Printf("true && false: %t\n", true && false)
fmt.Printf("true || false: %t\n", true || false)
fmt.Printf("!true: %t\n", !true)
// 比较运算返回布尔值
fmt.Printf("5 > 3: %t\n", 5 > 3)
fmt.Printf("\"abc\" == \"def\": %t\n", "abc" == "def")
// 布尔值不能与数字直接转换
// var i int = b1 // 错误!
// 需要显式转换
var i int
if b1 {
i = 1
} else {
i = 0
}
fmt.Printf("布尔转整数: %d\n", i)
}🎨 类型零值
零值概念
go
func main() {
// Go 语言中所有类型都有零值(默认值)
var i int // 0
var f float64 // 0.0
var b bool // false
var s string // ""(空字符串)
var p *int // nil
var slice []int // nil
var m map[string]int // nil
var ch chan int // nil
var fn func() // nil
fmt.Printf("int零值: %d\n", i)
fmt.Printf("float64零值: %g\n", f)
fmt.Printf("bool零值: %t\n", b)
fmt.Printf("string零值: %q\n", s)
fmt.Printf("指针零值: %v\n", p)
fmt.Printf("切片零值: %v\n", slice)
fmt.Printf("映射零值: %v\n", m)
fmt.Printf("通道零值: %v\n", ch)
fmt.Printf("函数零值: %v\n", fn)
}🔄 类型转换
基本类型转换
go
import "strconv"
func main() {
// 数值类型之间的转换(必须显式转换)
var i int = 42
var f float64 = float64(i) // int 转 float64
var u uint = uint(f) // float64 转 uint
fmt.Printf("int: %d, float64: %g, uint: %d\n", i, f, u)
// 字符串与数值的转换
str := "123"
num, err := strconv.Atoi(str) // 字符串转整数
if err != nil {
fmt.Printf("转换错误: %v\n", err)
} else {
fmt.Printf("字符串 %s 转整数: %d\n", str, num)
}
// 数值转字符串
numStr := strconv.Itoa(456)
fmt.Printf("整数456转字符串: %s\n", numStr)
// 浮点数转换
floatStr := strconv.FormatFloat(3.14159, 'f', 2, 64)
fmt.Printf("浮点数转字符串: %s\n", floatStr)
pi, err := strconv.ParseFloat("3.14159", 64)
if err == nil {
fmt.Printf("字符串转浮点数: %g\n", pi)
}
}类型断言
go
func main() {
// 接口类型的类型断言
var i interface{} = 42
// 安全的类型断言
if v, ok := i.(int); ok {
fmt.Printf("i 是 int 类型,值为: %d\n", v)
}
// 类型断言失败的情况
if v, ok := i.(string); ok {
fmt.Printf("i 是 string 类型,值为: %s\n", v)
} else {
fmt.Println("i 不是 string 类型")
}
// 危险的类型断言(可能panic)
// v := i.(string) // 如果 i 不是 string 类型会 panic
}📊 类型信息
获取类型信息
go
import (
"fmt"
"reflect"
)
func main() {
// 使用 reflect 包获取类型信息
var i int = 42
var f float64 = 3.14
var s string = "hello"
var b bool = true
fmt.Printf("i 的类型: %T, 值: %v\n", i, i)
fmt.Printf("f 的类型: %T, 值: %v\n", f, f)
fmt.Printf("s 的类型: %T, 值: %v\n", s, s)
fmt.Printf("b 的类型: %T, 值: %v\n", b, b)
// 使用 reflect 包
fmt.Printf("i 的反射类型: %v\n", reflect.TypeOf(i))
fmt.Printf("f 的反射类型: %v\n", reflect.TypeOf(f))
// 检查类型种类
t := reflect.TypeOf(i)
fmt.Printf("i 的种类: %v\n", t.Kind())
}🏷️ 自定义类型
类型定义
go
// 定义新类型
type MyInt int
type MyString string
type Celsius float64 // 摄氏度
type Fahrenheit float64 // 华氏度
func main() {
var temp1 Celsius = 25.0
var temp2 Fahrenheit = 77.0
fmt.Printf("摄氏度: %.1f°C\n", temp1)
fmt.Printf("华氏度: %.1f°F\n", temp2)
// 自定义类型需要显式转换
var regular float64 = float64(temp1)
fmt.Printf("转换为 float64: %.1f\n", regular)
// 不同自定义类型之间也需要转换
// temp1 = temp2 // 错误!不同类型
temp1 = Celsius((temp2 - 32) * 5 / 9) // 华氏度转摄氏度
fmt.Printf("转换后的摄氏度: %.1f°C\n", temp1)
}
// 为自定义类型添加方法
func (c Celsius) ToFahrenheit() Fahrenheit {
return Fahrenheit(c*9/5 + 32)
}
func (f Fahrenheit) ToCelsius() Celsius {
return Celsius((f - 32) * 5 / 9)
}类型别名
go
// 类型别名(Go 1.9+)
type MyInt2 = int // MyInt2 是 int 的别名
func main() {
var a int = 10
var b MyInt2 = 20
// 类型别名可以直接赋值,无需转换
a = b // 正确
b = a // 正确
fmt.Printf("a: %d, b: %d\n", a, b)
fmt.Printf("a 的类型: %T\n", a)
fmt.Printf("b 的类型: %T\n", b) // 显示为 int,不是 MyInt2
}🎯 类型使用最佳实践
类型选择指南
go
func main() {
// 1. 整数类型选择
var count int = 100 // 一般情况使用 int
var age uint8 = 25 // 确定范围较小且非负时使用 uint8
var id int64 = 1234567890 // 需要大范围时使用 int64
// 2. 浮点数类型选择
var price float64 = 99.99 // 一般使用 float64(精度更高)
var ratio float32 = 0.5 // 内存敏感场景使用 float32
// 3. 字符串 vs 字符
var name string = "张三" // 文本使用 string
var initial rune = '张' // 单个字符使用 rune
// 4. 布尔类型
var isActive bool = true // 标志状态使用 bool
fmt.Printf("计数: %d, 年龄: %d, ID: %d\n", count, age, id)
fmt.Printf("价格: %.2f, 比例: %.1f\n", price, ratio)
fmt.Printf("姓名: %s, 首字母: %c\n", name, initial)
fmt.Printf("状态: %t\n", isActive)
}类型安全示例
go
// 使用类型增强代码安全性
type UserID int
type ProductID int
func GetUser(id UserID) string {
return fmt.Sprintf("用户%d", id)
}
func GetProduct(id ProductID) string {
return fmt.Sprintf("产品%d", id)
}
func main() {
var userID UserID = 123
var productID ProductID = 456
fmt.Println(GetUser(userID)) // 正确
fmt.Println(GetProduct(productID)) // 正确
// 下面的调用会编译错误,增强了类型安全
// fmt.Println(GetUser(productID)) // 错误!类型不匹配
// fmt.Println(GetProduct(userID)) // 错误!类型不匹配
// 需要显式转换
fmt.Println(GetUser(UserID(productID))) // 显式转换后正确
}🎓 小结
本章我们详细学习了 Go 语言的数据类型系统:
- ✅ 数值类型:整数、浮点数、复数的详细使用
- ✅ 字符串类型:字符串操作和字符处理
- ✅ 布尔类型:逻辑值的使用
- ✅ 零值概念:理解类型的默认值
- ✅ 类型转换:显式转换和类型断言
- ✅ 自定义类型:类型定义和类型别名
- ✅ 最佳实践:类型选择和类型安全
理解 Go 的类型系统是编写高质量 Go 代码的基础。Go 的强类型系统帮助我们在编译时发现错误,提高代码的可靠性。
接下来,我们将学习 Go 语言变量,深入了解变量的声明、初始化和使用。
类型使用建议
- 优先使用
int、float64、string、bool等基本类型 - 在需要特定大小或范围时选择具体的类型
- 使用自定义类型增强代码的可读性和类型安全性
- 注意字符串的字节长度和字符长度的区别