Go 数据类型
类型系统是编程语言的骨架——它决定了变量能存什么、占多少内存、能做什么运算。Go 是静态强类型语言:每个变量都有明确的类型,编译期就检查,且没有隐式转换。本章把 Go 的数据类型从大类到细节全过一遍。
Go 类型的四大分类
Go 把数据类型分为四大类:
- 布尔型:
bool - 数值类型:整型、浮点型、复数
- 字符串:
string - 派生类型:指针、数组、切片、结构体、函数、通道、接口、Map
1. 布尔型 bool
最简单的类型,只有两个值:true 和 false。占用 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 的整型按是否有符号和位宽细分得很清楚,目的是让内存占用精确可控(写底层代码时很重要):
- 有符号:
int8int16int32int64,以及平台相关的int(在 64 位机上等于 int64)。 - 无符号:
uint8uint16uint32uint64,以及uint。 - 别名:
byte=uint8(一个字节);rune=int32(一个 Unicode 码点)。 - 还有
uintptr(存指针地址,给 unsafe 包用)。
// 整型按位宽和有无符号分多种
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新手建议:日常编码默认用 int 和 uint 就行,只有特殊场景(网络协议、文件格式、节省内存)才需要精确位宽。
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复数类型 complex64 和 complex128 平时用得很少,主要在信号处理、科学计算领域才会碰到。
4. 字符串 string
Go 的字符串有几个鲜明特点:
- UTF-8 编码:原生支持多语言字符,
"中文"完全合法。 - 不可变(immutable):和 Java/Python 一样,字符串一旦创建就不能修改其中的字符——拼接、修改都会生成新串。
- 两种字面量:双引号
"..."(支持转义,如\n);反引号`...`(原始字符串,不转义、保留换行,适合写 SQL/HTML/正则模板)。
// 双引号字符串:支持转义
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}
// 通道、接口会在后面章节详讲各派生类型一句话总结:
- 指针(
*T):存变量的地址,避免大对象拷贝、支持修改原变量。 - 数组(
[N]T):定长,长度也是类型的一部分([3]int和[4]int是不同类型)。 - 切片(
[]T):动态长度,Go 中比数组常用得多,类似 Python 的 list。 - 结构体(
struct):把多个字段打包成一个类型,面向对象的基础。 - 函数(
func):函数也是一等公民,可赋值、可传参。 - Map(
map[K]V):键值对,类似 Python 的 dict。 - 通道(
chan T):goroutine 之间通信的管道。 - 接口(
interface):定义一组方法签名,实现多态。
6. 类型转换:必须显式
这是 Go 和 C/Java 一个重大区别:Go 没有隐式类型转换。哪怕 int 和 int64 数值上相等,也不能直接相互赋值或一起运算——必须显式写 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)
有两个内置的别名你已经见过了:
byte是uint8的别名,二者完全等价。rune是int32的别名,专门用来表示 Unicode 码点。
你也可以用 type 关键字自定义类型:
- 类型定义:
type MyInt int——创建一个新类型,和int不能直接混用。 - 类型别名:
type MyInt = int——只是int的另一个名字,可以混用。
区别在于前者创建新类型(用于领域建模,如 type UserID int),后者只是给已有类型起个外号。
小结
这一章覆盖了 Go 的全部基础数据类型。记住三句话:布尔只有 true/false;数值按位宽分得很细;string 是 UTF-8 不可变。派生类型会在后面各章节展开。下一篇我们看变量——怎么把这些类型存起来。
← 上一篇 Go 基础语法
下一篇 Go 变量 →