Skip to content

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 语言变量,深入了解变量的声明、初始化和使用。

类型使用建议

  • 优先使用 intfloat64stringbool 等基本类型
  • 在需要特定大小或范围时选择具体的类型
  • 使用自定义类型增强代码的可读性和类型安全性
  • 注意字符串的字节长度和字符长度的区别

本站内容仅供学习和研究使用。