Go 数据类型

类型系统是编程语言的骨架——它决定了变量能存什么、占多少内存、能做什么运算。Go 是静态强类型语言:每个变量都有明确的类型,编译期就检查,且没有隐式转换。本章把 Go 的数据类型从大类到细节全过一遍。

Go 类型的四大分类

Go 把数据类型分为四大类:

1. 布尔型 bool

最简单的类型,只有两个值:truefalse。占用 1 字节。默认值是 false

package main

import "fmt"

func main() {
    // 布尔型:只有 true / false 两个值,默认 false
    var b1 bool        // 默认值 false
    var b2 bool = true
    b3 := 3 > 2        // 关系运算结果是 bool

    fmt.Println(b1, b2, b3)
    // 输出: false true true

    // 布尔值常用在 if/for 条件里
    if b2 {
        fmt.Println("b2 为真")
    }
}

2. 整型

Go 的整型按是否有符号位宽细分得很清楚,目的是让内存占用精确可控(写底层代码时很重要):

// 整型按位宽和有无符号分多种
var i int       = 42     // 有符号整型,平台相关(32 位机上是 int32,64 位机上是 int64)
var u uint      = 42     // 无符号整型,只能存非负数

// 明确位宽的类型
var i8  int8    = 127    // 范围 -128 ~ 127
var i16 int16   = 32767
var i32 int32   = 1 << 30
var i64 int64   = 1 << 60

var u8  uint8   = 255    // 范围 0 ~ 255
var u16 uint16  = 65535
var u32 uint32  = 1 << 31
var u64 uint64  = 1 << 62

// 两个常用别名
var b byte = 65          // byte 是 uint8 的别名,常表示 ASCII 字符
var r rune = '中'        // rune 是 int32 的别名,表示一个 Unicode 码点

fmt.Println(i, u, i8, b, r)
// 输出: 42 42 127 65 20013

新手建议:日常编码默认用 intuint 就行,只有特殊场景(网络协议、文件格式、节省内存)才需要精确位宽。

3. 浮点型与复数

浮点只有两种:float32(4 字节,约 7 位有效数字)和 float64(8 字节,约 15 位有效数字)。默认推荐用 float64,因为它的精度和大多数数学库匹配。

// 浮点型只有两种位宽
var f1 float32 = 3.14
var f2 float64 = 3.141592653589793  // 默认推荐用 float64(精度更高)

// 科学计数法
var e1 = 1e6     // 1000000.0,float64
var e2 = 1.5e-3  // 0.0015

// 复数(用得少,科学计算场景才需要)
var c1 complex64  = 1 + 2i
var c2 complex128 = 3 + 4i
fmt.Println(real(c2), imag(c2))   // 输出实部和虚部: 3 4

复数类型 complex64complex128 平时用得很少,主要在信号处理、科学计算领域才会碰到。

4. 字符串 string

Go 的字符串有几个鲜明特点:

// 双引号字符串:支持转义
s1 := "Hello\nGo"
fmt.Println(s1)
// Hello
// Go

// 反引号字符串:原始字符串,不转义、可跨多行
// 常用于写 HTML、SQL、正则表达式模板
s2 := `第一行
第二行
第三行`
fmt.Println(s2)

// 字符串拼接
s3 := "Hello" + " " + "Go"
s4 := fmt.Sprintf("%s, %d", "count", 42)

// 字符串长度:len 返回字节数(不是字符数!)
fmt.Println(len("abc"))    // 3
fmt.Println(len("中文"))    // 6(UTF-8 编码下每个汉字 3 字节)

坑提醒len("中文") 返回 6 而不是 2,因为 len 数的是字节数不是字符数。要数字符得用 utf8.RuneCountInString("中文") 或转成 []rune

5. 派生类型一览

这些类型会在后续章节深入讲,这里先建立印象:

// 指针:存变量的内存地址
x := 42
p := &x              // & 取地址,*p 解引用
fmt.Println(*p)      // 42

// 数组:固定长度
var arr [3]int = [3]int{1, 2, 3}

// 切片:动态长度(Go 中比数组常用得多)
s := []int{1, 2, 3, 4}
s = append(s, 5)

// 结构体:自定义复合类型
type User struct {
    Name string
    Age  int
}
u := User{Name: "小明", Age: 18}

// 函数:func 也是一种类型
var add func(int, int) int = func(a, b int) int { return a + b }

// Map:键值对
m := map[string]int{"apple": 5, "banana": 3}

// 通道、接口会在后面章节详讲

各派生类型一句话总结:

6. 类型转换:必须显式

这是 Go 和 C/Java 一个重大区别:Go 没有隐式类型转换。哪怕 intint64 数值上相等,也不能直接相互赋值或一起运算——必须显式写 T(v)。这避免了大量"无声的精度丢失"bug。

// Go 没有隐式类型转换!必须显式写 T(v)
var i int     = 42
var f float64 = float64(i)   // int -> float64
var j int     = int(f)       // float64 -> int(截断小数)

// 字符串和其他类型互转:用 strconv 包
import "strconv"

n, _ := strconv.Atoi("42")     // 字符串 -> int
s  := strconv.Itoa(42)         // int -> 字符串
b, _ := strconv.ParseBool("true")  // 字符串 -> bool

// 字符串和字节切片互转
bytes := []byte("hello")      // string -> []byte
str   := string(bytes)        // []byte -> string

// 注意:不同整型之间也要显式转换
var a int32 = 10
var b int   = int(a)   // int32 和 int 不能直接运算

7. 类型别名(type alias)

有两个内置的别名你已经见过了:

你也可以用 type 关键字自定义类型:

区别在于前者创建新类型(用于领域建模,如 type UserID int),后者只是给已有类型起个外号。

小结

这一章覆盖了 Go 的全部基础数据类型。记住三句话:布尔只有 true/false;数值按位宽分得很细;string 是 UTF-8 不可变。派生类型会在后面各章节展开。下一篇我们看变量——怎么把这些类型存起来。

← 上一篇 Go 基础语法

下一篇 Go 变量

✈️💬