03 - Go 常用类型速查表 + 实战建议(实战向)

文章目录


Go 常用类型速查表 + 实战建议(实战向)

在 Go 实战开发中,理解不同类型的语义、零值、拷贝行为,比记语法更重要。


基础值类型(Value Types)

整型(Integer)

有符号整型(可以表示负数)

类型 位数 取值范围 备注
int 32/64 -2³¹~2³¹-1(32位)或 -2⁶³~2⁶³-1(64位) 与平台相关
int8 8 -128 ~ 127 最小整型
int16 16 -32768 ~ 32767
int32 32 -2³¹ ~ 2³¹-1 别名 rune
int64 64 -2⁶³ ~ 2⁶³-1

无符号整型(只能表示非负数)

类型 位数 取值范围 备注
uint 32/64 0 ~ 2³²-1 或 0 ~ 2⁶⁴-1 与平台相关
uint8 8 0 ~ 255 别名 byte
uint16 16 0 ~ 65535
uint32 32 0 ~ 2³²-1
uint64 64 0 ~ 2⁶⁴-1
uintptr 32/64 0 ~ 2³²-1 或 0 ~ 2⁶⁴-1 用于底层指针操作

小结

  • 有符号整型可以存负数;无符号整型只能存 0 及正数。
  • intuint 会根据平台(32位/64位)变化大小。
  • runeint32 的别名,用于表示 Unicode 字符。
  • byteuint8 的别名,用于表示字节。

Go 里实际测试整型

下面的示例演示了各种整型的赋值、打印、类型转换

go 复制代码
package main

import (
    "fmt"
)

func main() {
    // 有符号整型
    var a int = -100
    var b int8 = 120
    var c int16 = -30000
    var d int32 = 100000
    var e int64 = -1000000000

    fmt.Println("有符号整型:")
    fmt.Println(a, b, c, d, e)

    // 无符号整型
    var ua uint = 100
    var ub uint8 = 200
    var uc uint16 = 50000
    var ud uint32 = 3000000000
    var ue uint64 = 100000000000

    fmt.Println("无符号整型:")
    fmt.Println(ua, ub, uc, ud, ue)

    // 类型转换
    sum := int32(b) + d // int8 -> int32 再加
    fmt.Println("sum =", sum)

    // 注意负数转无符号会变成大数(取值范围)
    negative := int8(-1)
    fmt.Println("int8(-1) =", negative) // 输出 -1
    u := uint8(negative)
    fmt.Println("uint8(-1) =", u) // 输出 255

    abc := int8(-2)
    cc := uint8(abc)
    fmt.Println("cc =", cc) // 输出 254
}

输出结果:

bash 复制代码
有符号整型:
-100 120 -30000 100000 -1000000000
无符号整型:
100 200 50000 3000000000 100000000000
sum = 100120
int8(-1) = -1
uint8(-1) = 255
cc = 254

实战建议

  • ✅ 业务字段:优先 int
  • ✅ ID、时间戳、计数:int64
  • ✅ 网络协议/文件格式:显式 int32 / uint32
  • ❌ 不要滥用 uint(容易引入负数比较 Bug)

浮点型(Float)

Go 浮点型概念

类型 精度 说明
float32 约 6~7 位十进制有效数字 占 4 字节
float64 约 15~16 位十进制有效数字 占 8 字节,Go 默认浮点类型

特点:

  • 浮点数可以表示小数,也能表示非常大的数和非常小的数(科学计数法)。
  • 浮点数有 精度限制 ,不能精确表示所有小数,例如 0.1 在计算机里不是精确的 0.1。

基本用法

go 复制代码
package main

import (
    "fmt"
)

func main() {
    var a float32 = 3.1415926
    var b float64 = 3.14159265358979323846

    fmt.Println("float32:", a) // 精度有限,输出 3.1415925
    fmt.Println("float64:", b) // 精度更高,输出 3.141592653589793

    // 科学计数法
    c := 1.23e4 // 1.23 × 10^4 = 12300.0
    d := 5.67e-3 // 5.67 × 10^-3 = 0.00567
    fmt.Println("c:", c)
    fmt.Println("d:", d)
}

输出结果:

bash 复制代码
float32: 3.1415925
float64: 3.141592653589793
c: 12300
d: 0.00567

类型转换

  • float32float64 不能直接运算,需要显式转换:
go 复制代码
func main() {
    var f32 float32 = 1.5
    var f64 float64 = 2.7

    sum := float64(f32) + f64
    fmt.Println(sum)
}
  • 浮点数和整数也需要转换:
go 复制代码
func main() {
    i := 3
    f := 2.5
    sum := float64(i) + f
    fmt.Println(sum) // 5.5
}

注意事项

  1. 浮点数计算可能有精度误差:
go 复制代码
fmt.Println(0.1 + 0.2) // 输出 0.30000000000000004
  1. 默认写浮点数时,Go 会用 float64
go 复制代码
f := 3.14 // 默认 float64

布尔型(Bool)

基本概念

  • 类型:bool
  • 取值:truefalse
  • 不能把整数或其他类型当作布尔值(没有 0 = false, 非 0 = true 的魔法转换)
go 复制代码
var flag bool = true
fmt.Println(flag) // true

flag = false
fmt.Println(flag) // false

条件判断

go 复制代码
package main

import "fmt"

func main() {
    a := 10
    b := 20

    // 比较表达式会得到 bool
    isGreater := a > b
    fmt.Println(isGreater) // false

    if isGreater { // 直接用布尔值if isGreater == true { ... } 也可以,但没必要
        fmt.Println("a 大于 b")
    } else {
        fmt.Println("a 不大于 b")
    }
}

输出结果:

bash 复制代码
false
a 不大于 b
  • 注意:你 不能if a { ... },会报错,因为 a 是整数,不是布尔值。
  • 必须写成 if a != 0 { ... } 或直接用比较表达式。

与 C/C++ 的不同

语言 隐式转换
C/C++ int 0 = false, 非 0 = true
Go ❌ 没有隐式转换,必须是 bool

这种设计让 Go 的条件判断更安全,不会因为数字或指针被误用而导致逻辑错误。


布尔运算

go 复制代码
package main

import (
    "fmt"
)

func main() {
    a := true
    b := false

    fmt.Println(a && b) // false 有假则为假
    fmt.Println(a || b) // true 有真则为真
    fmt.Println(!a)     // false 反义
}

输出结果:

bash 复制代码
false
true
false
  • 支持 &&(与)、||(或)、!(非)
  • 没有隐式类型转换,更加类型安全

字符串 & 字符(String / Rune / Byte)

注: 这里那个string与其他的区别是""''的区别

类型概览

类型 实际类型 用途
string 不可变字节序列 存储文本,UTF-8 编码
byte uint8 处理二进制数据,文件/网络 I/O
rune int32 表示一个 Unicode 字符,适合中文、emoji 等

核心知识点

字符串长度
go 复制代码
package main

import (
	"fmt"
)

func main() {
	s := "你好"                  // 2 个字符
	fmt.Println(len(s))         // 6 字节(每个中文 3 字节 UTF-8)
	fmt.Println(len([]rune(s))) // 2 个字符
}

解释:

  • len(s)字节长度(string 底层是 byte slice)
  • len([]rune(s))字符长度(Unicode 字符数)

输出结果:

bash 复制代码
6
2
字符串不可变
go 复制代码
s := "hello"
// s[0] = 'H' // ❌ 编译错误,string 是只读的

如果要修改:

go 复制代码
package main

import (
	"fmt"
)

func main() {
	s := "hello"
	b := []byte(s)     // 新建一个可变的 []byte 切片,指向 s 的底层数组
	// fmt.Println(b)     // [104 101 108 108 111](ASCII 码)
	b[0] = 'H'         // 可以修改
	b[4] = '!'         // 可以修改
	b = append(b, 'w') // 可以修改
	s2 := string(b)    // 再转换回 string
	fmt.Println(s2)    // Hello
}

输出结果:

bash 复制代码
Hell!w

rune 与 byte 的区别
go 复制代码
package main

import (
	"fmt"
	"reflect"
)

func main() {
	c := '你'       // '你' 用单引号括起来,是 字符常量,默认类型是 rune(也就是 int32)
	b := byte('A') // 'A' 用单引号括起来,byte('A') 强制转换为 uint8

	fmt.Println(c)
	fmt.Println(b)
	fmt.Println(reflect.TypeOf(c))
	fmt.Println(reflect.TypeOf(b))

	fmt.Printf("%c %T\n", c, c) // 你 int32,%c → 按 字符 打印(Unicode 字符)
	fmt.Printf("%c %T\n", b, b) // A uint8,%T → 打印变量类型
	var d rune = '你'
	fmt.Println(d) // 输出 Unicode 数值:20320
}

输出结果:

bash 复制代码
20320
65
int32
uint8
你 int32
A uint8
20320
复制代码
'你' 的 Unicode 编码是 U+4F60(十进制 20320)
byte(uint8)最大只能存 0~255
所以 '你' 必须用 rune/int32 才能存下完整 Unicode 值

'A' 的 ASCII 编码是 65
65 落在 0~255 范围内
所以可以安全存入 byte/uint8
  • byte 适合处理 ASCII 或二进制
  • rune 适合处理中文或多字节 Unicode 字符

实战建议

  1. 处理二进制 → 用 []byte

    go 复制代码
    data := []byte{0x01, 0x02, 0x03}

    输出结果:[1 2 3]

  2. 处理中文/字符 → 用 []rune

    go 复制代码
    s := "你好,Go"
    r := []rune(s)
    r[0] = '您'
    fmt.Println(string(r)) // 您好,Go
  3. 不要直接修改 string → string 是只读,必须通过转换为 []byte[]rune 再修改。


数组 vs 切片(Array vs Slice)

在 Go 中,数组(Array)和切片(Slice)都是用于存储一组数据的结构,但它们的设计理念和使用场景完全不同。

Array(数组:几乎不用)

go 复制代码
var a [3]int = [3]int{1, 2, 3}
特点(重点理解2个就够了)
1.长度是类型的一部分
go 复制代码
package main

import "fmt"

func main() {
	var a [3]int
	var b [4]int
	fmt.Println(a)
	fmt.Println(b)
}


// a 和 b 是完全不同的类型,不能互相赋值

输出结果:

bash 复制代码
[0 0 0]
[0 0 0 0]

👉 就像:

  • [3]int[4]int
  • 不能互相赋值或传参

2.是值拷贝(很坑)
go 复制代码
package main

import "fmt"

func main() {
	a := [3]int{1, 2, 3}
	b := a

	b[0] = 100

	fmt.Println(a) // [1 2 3]
	fmt.Println(b) // [100 2 3]
}

输出结果:

bash 复制代码
[1 2 3]
[100 2 3]

👉 修改 b,不影响 a(因为复制了一份)

  • 赋值会复制一份数据
  • 修改副本不会影响原数据

一句话:👉 数组 = 固定长度 + 拷贝传递 → 不灵活,所以很少用


Slice(切片:核心⭐⭐⭐⭐⭐)

go 复制代码
package main

import "fmt"

func main() {
	s := []int{1, 2, 3}
	b := append(s, 4)
	fmt.Println(b)
}

👉 输出:

go 复制代码
[1 2 3 4]

Slice 的本质(重点🔥)

切片本质上是对数组的一层封装:

go 复制代码
type slice struct {
    ptr *T  // 指向底层数组
    len int // 当前长度
    cap int // 容量
}

👉 可以理解成:

切片 =Slice = 引用 + 长度 + 容量


底层结构理解(很关键)
text 复制代码
s := []int{1,2,3}

底层其实是:

数组: [1 2 3 _ _]

s:
ptr → 指向数组第一个元素
len = 3
cap = 5(可能更大)

为什么 slice 修改会影响原数据?
go 复制代码
s1 := []int{1,2,3}
s2 := s1

s2[0] = 100

fmt.Println(s1) // [100 2 3]

👉 因为:

  • s1 和 s2 指向 同一个底层数组

append 的坑(面试常考🔥)
go 复制代码
s := []int{1,2,3}
s2 := s

s2 = append(s2, 4)
分两种情况:

情况1:没扩容(共用数组)
text 复制代码
原数组够用 → 直接追加

👉 修改会互相影响


❗情况2:发生扩容(重点)
text 复制代码
原数组不够 → 创建新数组

👉 此时:

  • s2 指向新数组
  • s 还是旧数组
  • 两者彻底分离 ❗

如何判断有没有扩容?

go 复制代码
fmt.Println(len(s), cap(s))

👉 如果 append 后 cap 变了,说明扩容了

go 复制代码
package main

import "fmt"

func main() {
	s := []int{1, 2, 3}
	s2 := s

	s2 = append(s2, 4)
	fmt.Println(s) // 输出 [1 2 3]
	fmt.Println(s2)
	fmt.Println(len(s), cap(s))
	fmt.Println(len(s2), cap(s2))
}

输出结果:

bash 复制代码
[1 2 3]
[1 2 3 4]
3 3
4 6

实战建议(非常重要)

  1. 函数参数统一用 slice

    go 复制代码
    func foo(s []int) {}

    👉 不要用数组!


  1. 想避免数据被改 → copy

    go 复制代码
    s2 := make([]int, len(s1))
    copy(s2, s1)

  1. 预分配容量(性能优化)

    go 复制代码
    s := make([]int, 0, 100)

    👉 避免频繁扩容


小总结(记住这3句话就够了)

  1. 数组:固定长度 + 值拷贝 → 基本不用
  2. 切片:引用底层数组 → 是核心
  3. append 可能换底层数组 → 会"断开关系"

Map(字典) & Struct(结构体)

在 Go 中,mapstruct 是两种非常核心的数据结构:

  • map:用于键值存储(类似字典)
  • struct:用于定义数据模型(类似对象)

Map(字典)

go 复制代码
package main

import "fmt"

func main() {
	m := make(map[string]int)
	m["a"] = 1
	m["b"] = 2
	fmt.Println(m)
	delete(m, "a")
	fmt.Println(m)
}

输出结果:

bath 复制代码
map[a:1 b:2]
map[b:2]

常见定义方式

go 复制代码
map[string]int   // key: string, value: int
map[int]string   // key: int, value: string

Map 基本操作

go 复制代码
package main

import "fmt"

func main() {
	m := make(map[string]int)

	m["a"] = 1          // 添加/修改
	fmt.Println(m)      // 获取
	fmt.Println(m["a"]) // 获取

	delete(m, "a") // 删除
	fmt.Println(m)
}

输出结果:

bath 复制代码
map[a:1]
1
map[]

核心坑点(必会🔥)

❌ 未初始化直接使用 → panic
go 复制代码
var m map[string]int
m["a"] = 1   // panic: assignment to entry in nil map

👉 原因:

  • var m map[...] 只是声明
  • 此时 m == nil
  • 不能直接写入数据

✅ 正确写法
go 复制代码
m := make(map[string]int)

或者:

go 复制代码
m := map[string]int{
    "a": 1,
    "b": 2,
}

判断 key 是否存在(非常重要)

go 复制代码
package main

import "fmt"

func main() {
	m := make(map[string]int)
	value, ok := m["a"]
	if ok {
		fmt.Println("存在:", value)
	} else {
		fmt.Println("不存在")
	}

	m["a"] = 1
	value, ok = m["a"]
	if ok {
		fmt.Println("存在:", value)
	} else {
		fmt.Println("不存在")
	}
}

输出结果:

bath 复制代码
不存在
存在: 1

👉 这是 Go 的经典写法(面试必问)


Map 特点总结

  • 引用类型(类似指针)
  • 无序(遍历顺序随机)
  • 读不存在的 key → 返回零值(不会报错)
  • 写 nil map → panic

Struct(结构体)

go 复制代码
type User struct {
    ID   int
    Name string
    Age  int
}

👉 用于定义一类数据结构(类似 Java 的 class)


Struct 使用方式

Go 复制代码
package main

import "fmt"

func main() {
	type User struct {
		ID   int
		Name string
		Age  int
	}
	u1 := User{1, "Tom", 18}
     //初始化
	u2 := User{
		ID:   2,
		Name: "Jerry",
		Age:  20,
	}
	fmt.Println(u1)
	fmt.Println(u2)
	// 访问字段
	fmt.Println(u1.Name)
}

输出结果:

bath 复制代码
{1 Tom 18}
{2 Jerry 20}
Tom

Struct 是值类型(重点)

go 复制代码
package main

import "fmt"

func main() {
	type User struct {
		ID   int
		Name string
		Age  int
	}
	u1 := User{1, "Tom", 18}
	u2 := u1

	u2.Name = "Jack"
	fmt.Println(u1)      // {1 Tom 18}
	fmt.Println(u2)      // {1 Jack 18}
	fmt.Println(u1.Name) // Tom
	fmt.Println(u2.Name) // Jack
}

输出结果:

bath 复制代码
{1 Tom 18}
{1 Jack 18}
Tom
Jack

👉 修改 u2 不影响 u1(发生了拷贝)


✅ 想共享数据 → 用指针
go 复制代码
package main

import "fmt"

func main() {
	type User struct {
		ID   int
		Name string
		Age  int
	}
	u1 := User{1, "Tom", 18}

	fmt.Println(u1) // 输出:{1 Tom 18}

	u2 := &u1
	u2.Name = "Jack"

	fmt.Println(u2)
	fmt.Println(u1.Name)
	fmt.Println(u2.Name)
}

输出结果:

bath 复制代码
{1 Tom 18}
&{1 Jack 18}
Jack
Jack

Struct 实战建议

👉 在实际开发中,struct 几乎无处不在:

  • 领域模型(User、Order)
  • DTO(接口请求/响应)
  • 配置文件(YAML/JSON)
  • 数据库存储结构

总结

Map

  • 必须 make 初始化
  • 是引用类型
  • 常用于键值存储

Struct

  • 是值类型(默认拷贝)
  • 用于定义数据结构
  • 想共享 → 用指针

实战总结(专栏金句版)

Go 的类型系统可以分为:

基础值类型(int、float64、bool、string)

复合类型(slice、map、struct)

抽象与引用类型(pointer、interface、chan、func)

实战中,80% 时间用的是:
int、string、[]T、map、struct、error、interface


Go 常用类型:最小实战例子

1️⃣ string / rune / byte 差异

go 复制代码
package main

import "fmt"

func main() {
    s := "你好Go"

    fmt.Println("len(s):", len(s))
    fmt.Println("len([]rune(s)):", len([]rune(s)))

    for i := 0; i < len(s); i++ {
        fmt.Printf("%x ", s[i])
    }
    fmt.Println()

    for _, r := range s {
        fmt.Printf("%c ", r)
    }
}

输出结果:

bath 复制代码
len(s): 8
len([]rune(s)): 4
e4 bd a0 e5 a5 bd 47 6f 
你 好 G o 

2️⃣ slice 扩容 & 引用语义(高频坑)

go 复制代码
package main

import "fmt"

func main() {
    s := make([]int, 0, 2)
    fmt.Printf("addr=%p len=%d cap=%d\n", s, len(s), cap(s))

    s = append(s, 1, 2)
    fmt.Printf("addr=%p len=%d cap=%d\n", s, len(s), cap(s))

    s = append(s, 3)
    fmt.Printf("addr=%p len=%d cap=%d\n", s, len(s), cap(s))
}

输出结果:

bath 复制代码
addr=0xc000098040 len=0 cap=2
addr=0xc000098040 len=2 cap=2
addr=0xc0000ba000 len=3 cap=4

3️⃣ map nil panic(经典)

go 复制代码
package main

func main() {
    var m map[string]int
    m["a"] = 1   // panic:尝试对空 map 中的条目赋值
}

对比正确写法:

go 复制代码
package main

import "fmt"

func main() {
	m := make(map[string]int)
	m["a"] = 1
	fmt.Println(m) // map[a:1]
}

👉 运维 / 后端新手最常见坑之一


4️⃣ struct 值拷贝 vs 指针

go 复制代码
package main

import "fmt"

type User struct {
	Name string
}

func modifyByValue(u User) {
	u.Name = "Jerry"
}

func modifyByPointer(u *User) {
	u.Name = "Jerry"
}

func main() {
	u := User{Name: "Tom"}

	modifyByValue(u)
	fmt.Println(u.Name) // Tom

	modifyByPointer(&u)
	fmt.Println(u.Name) // Jerry
}

5️⃣ interface 动态类型

go 复制代码
package main

import "fmt"

func printType(v interface{}) {
	fmt.Printf("type=%T, value=%v\n", v, v)
}

func main() {
	printType(10)
	printType("hello")
	printType([]int{1, 2})
}

输出:

复制代码
type=int, value=10
type=string, value=hello
type=[]int, value=[1 2]

6️⃣ nil slice vs empty slice(很专业)

go 复制代码
func main() {
    var s1 []int
    s2 := []int{}

    fmt.Println(s1 == nil) // true
    fmt.Println(s2 == nil) // false
    fmt.Println(len(s1), len(s2)) // 0 0
}
相关推荐
爱码驱动2 小时前
Java多线程详解(5)
java·开发语言·多线程
@atweiwei2 小时前
用 Rust 构建 LLM 应用的高性能框架
开发语言·后端·ai·rust·langchain·llm
九转成圣2 小时前
实战记录:用 Java 拼接长图/网格图,我踩了哪些坑?
java·开发语言
lzhdim2 小时前
SQL 入门 9:SQL 高级子查询:ANY、EXISTS 与多位置应用
java·开发语言·数据库·sql·mysql
Dream of maid2 小时前
Python(11) 进程与线程
开发语言·python
cici158742 小时前
非线性模型预测控制(NMPC)基于CasADi的MATLAB实现
开发语言·matlab
独特的螺狮粉3 小时前
开源鸿蒙跨平台Flutter开发:量子态波函数坍缩系统-波动力学与概率云渲染架构
开发语言·flutter·华为·架构·开源·harmonyos
冰暮流星3 小时前
javascript之dom访问属性
开发语言·javascript·dubbo
lsx2024063 小时前
SQL Auto Increment 自动增长
开发语言