Go入门:基本数据类型全览与选择指南

Go入门:基本数据类型全览与选择指南

大家好,我是你们的Go语言向导。前面我们学习了变量声明和零值机制,这些知识都建立在一个基础之上------Go的类型系统。今天我们就来全面了解Go语言的基本数据类型,掌握每种类型的特点和适用场景。

💡 类型系统是一门语言的"骨架"。Go的类型系统简洁但不简单------它只有25个关键字,但类型体系覆盖了从系统编程到应用开发的各种需求。理解每种类型的设计意图,你才能在正确的场景选择正确的类型。

一、Go类型系统全景

1.1 类型分类总览

Go语言的类型可以分为四大类:

复制代码
Go类型体系
├── 基本类型(Basic Types)
│   ├── 布尔型(bool)
│   ├── 数值型
│   │   ├── 整数型(int, int8~64, uint, uint8~64, uintptr)
│   │   ├── 浮点型(float32, float64)
│   │   ├── 复数型(complex64, complex128)
│   │   └── 别名型(byte = uint8, rune = int32)
│   └── 字符串型(string)
│
├── 复合类型(Composite Types)
│   ├── 数组(Array)
│   ├── 切片(Slice)
│   ├── 映射(Map)
│   ├── 结构体(Struct)
│   └── 指针(Pointer)
│
├── 引用类型(Reference Types)
│   ├── 切片
│   ├── 映射
│   ├── 通道(Channel)
│   └── 函数(Function)
│
└── 接口类型(Interface Types)

1.2 为什么Go类型这么简洁

Go没有以下在其他语言中常见的类型:

  • 没有 char --- 用 rune (int32) 或 byte (uint8)
  • 没有 long / long long --- 用 int32 / int64
  • 没有 double --- 用 float64
  • 没有 unsigned short 等各种组合 --- Go的命名更具可读性

二、布尔类型

2.1 基本用法

go 复制代码
var isActive bool = true
var isClosed bool = false
var isAdmin = true  // 类型推断

// 零值
var flag bool  // 零值为 false

2.2 布尔运算

go 复制代码
// 逻辑运算
result := true && false   // false (AND)
result = true || false    // true  (OR)
result = !true            // false (NOT)

// 比较运算产生布尔值
isEqual := (5 == 5)       // true
isGreater := (10 > 5)     // true
isLessOrEqual := (3 <= 3) // true

2.3 关键注意点

go 复制代码
// ⚠️ Go中bool与整数不互通
var flag bool = true
// var i int = int(flag)        // ❌ 编译错误
// var flag2 bool = bool(1)     // ❌ 编译错误
// if 1 { }                      // ❌ 编译错误

// ✅ Go要求显式的布尔表达式
if isActive {
    // ...
}

// ❌ 其他语言的习惯在Go中不适用
// if count { }     // ❌ 编译错误
// if !count { }    // ❌ 编译错误
if count > 0 {      // ✅ 必须写完整的比较
    // ...
}

💡 Go的布尔类型非常"严格"------它拒绝一切隐式转换。这种设计避免了C/C++中因整数与布尔值混用导致的大量Bug。

三、整数类型

3.1 整数类型一览

Go提供了丰富的整数类型,覆盖了各种位宽需求:

类型 位宽 范围
int8 8位 -128 ~ 127
int16 16位 -32768 ~ 32767
int32 32位 -2^31 ~ 2^31-1
int64 64位 -2^63 ~ 2^63-1
uint8 8位 0 ~ 255
uint16 16位 0 ~ 65535
uint32 32位 0 ~ 2^32-1
uint64 64位 0 ~ 2^64-1
int 平台相关(32/64) 取决于平台
uint 平台相关(32/64) 取决于平台
uintptr 足够存下指针 取决于平台
byte 8位(uint8别名) 0 ~ 255
rune 32位(int32别名) 0 ~ 0x10FFFF

3.2 整数类型的选择指南

go 复制代码
// ── 日常编程 ──
var count int = 100          // 推荐:用int作为默认整数类型
var index int = 0

// ── 明确需要特定位宽 ──
var fileSize int64 = 1024    // 文件大小(可能非常大)
var timestamp int64 = time.Now().Unix() // Unix时间戳
var port int32 = 8080        // 网络端口(16位就够了,但32位更安全)

// ── 与系统交互 ──
var id uint32 = 12345        // 数据库中的UNSIGNED INT
var flags uint16 = 0xFFFF    // 标志位
var uid uint64 = 10000000001 // 分布式ID

// ── 字节处理 ──
var b byte = 'A'             // 单个字节(ASCII字符)
var data []byte = []byte("hello")  // 字节切片

// ── Unicode字符 ──
var ch rune = '中'            // 单个Unicode字符
var chars []rune = []rune("你好")  // rune切片

// ── 数组索引和长度 ──
var length int = len(slice)  // len()返回int
// 注意:如果使用其他整数类型做索引,需要转换

3.3 int vs int64

这是Go新手常问的一个问题:

go 复制代码
// int 是Go的"默认"整数类型
// - 在32位系统上是32位
// - 在64位系统上是64位

// 选择建议:
// 1. 普通变量、计数器、索引 → int
// 2. 需要"很大"的整数 → int64
// 3. 序列化/反序列化(JSON、protobuf)→ int64(保证一致性)
// 4. 时间相关 → int64
go 复制代码
// ⚠️ int和int64不能直接互操作
var a int = 10
var b int64 = 20
// c := a + b        // ❌ 编译错误:类型不匹配
c := int64(a) + b    // ✅ 需要显式转换

// 这在循环中尤其常见
slice := []int{1, 2, 3}
var index int64 = 0
// slice[index]       // ❌ 编译错误
slice[int(index)]     // ✅ 显式转换

3.4 整数运算的溢出

go 复制代码
// ⚠️ 整数溢出不会引发panic,而是静默回绕
var max uint8 = 255
max = max + 1     // max = 0(回绕!)

var min int8 = -128
min = min - 1     // min = 127(回绕!)

// ✅ 如何安全处理
func SafeAddUint8(a, b uint8) (uint8, error) {
    if a > 255 - b {
        return 0, fmt.Errorf("uint8 overflow: %d + %d", a, b)
    }
    return a + b, nil
}

四、浮点数类型

4.1 float32与float64

go 复制代码
var f32 float32 = 3.141592653589793  // 大约7位有效数字
var f64 float64 = 3.141592653589793  // 大约15位有效数字

fmt.Printf("float32: %.15f\n", f32)  // 3.141592741012573
fmt.Printf("float64: %.15f\n", f64)  // 3.141592653589793

📝 选择指南:

  • float64:默认选择,精度足够大多数计算
  • float32:内存受限场景(大量数组)、图形处理
  • 不要用浮点数表示金额(见下文陷阱)

4.2 浮点数的特殊值

go 复制代码
var inf = math.Inf(1)       // 正无穷
var negInf = math.Inf(-1)   // 负无穷
var nan = math.NaN()        // Not a Number

// ⚠️ NaN不等于任何值,包括它自己
fmt.Println(nan == nan)           // false
fmt.Println(math.IsNaN(nan))      // true

// 无穷的运算
fmt.Println(1.0 / 0.0)            // +Inf
fmt.Println(-1.0 / 0.0)           // -Inf
fmt.Println(0.0 / 0.0)            // NaN

4.3 浮点数精度陷阱

go 复制代码
// ⚠️ 浮点数比较的经典陷阱
var a = 0.1
var b = 0.2
var c = 0.3

// 错误:直接用 == 比较
fmt.Println(a+b == c)  // false!(因为0.1+0.2 != 0.3在二进制浮点中)

// 原因:0.1、0.2、0.3在二进制中都是无限循环小数
fmt.Printf("%.20f\n", 0.1+0.2)  // 0.30000000000000004441...
fmt.Printf("%.20f\n", 0.3)      // 0.29999999999999998890...

// ✅ 正确的比较方法:使用很小的误差范围
const epsilon = 1e-9
func floatEqual(a, b float64) bool {
    return math.Abs(a-b) < epsilon
}
fmt.Println(floatEqual(a+b, c))  // true
go 复制代码
// ⚠️ 金融计算:绝不能用浮点数!
// 错误示例
var price float64 = 19.90
var quantity float64 = 100
var total = price * quantity  // 1989.99999999... 而不是 1990.00

// ✅ 正确做法:用整数表示分(或厘)
var priceInCents int64 = 1990  // 19.90元 = 1990分
var quantity2 int64 = 100
var totalInCents int64 = priceInCents * quantity2  // 199000分 = 1990.00元
fmt.Printf("总价: %d.%02d元\n", totalInCents/100, totalInCents%100)

五、复数类型

5.1 基本使用

go 复制代码
var c1 complex128 = 3 + 4i
var c2 complex64 = complex(1, 2)  // 1+2i
c3 := 5 - 3i                      // 类型推断为 complex128

// 实部和虚部
fmt.Println(real(c1))  // 3
fmt.Println(imag(c1))  // 4

// 复数运算
sum := c1 + c3          // (3+4i) + (5-3i) = (8+1i)
product := c1 * c3      // (3+4i) * (5-3i) = 27+11i
abs := cmplx.Abs(c1)     // 模 = sqrt(3²+4²) = 5

5.2 复数在Go中的应用

Go是为数不多内置复数类型的语言,这在科学计算中非常有用:

go 复制代码
// 傅里叶变换、信号处理、量子力学等场景
func DFT(input []complex128) []complex128 {
    n := len(input)
    output := make([]complex128, n)
    for k := 0; k < n; k++ {
        var sum complex128
        for t := 0; t < n; t++ {
            angle := -2 * math.Pi * float64(k) * float64(t) / float64(n)
            sum += input[t] * complex(math.Cos(angle), math.Sin(angle))
        }
        output[k] = sum
    }
    return output
}

六、字符串类型

6.1 字符串的本质

在Go中,字符串是一个不可变的字节序列

go 复制代码
var s string = "Hello, Go"
fmt.Println(len(s))  // 9(字节数,不是字符数)

// 字符串的底层结构(概念上):
// type stringStruct struct {
//     str unsafe.Pointer  // 指向底层字节数组
//     len int             // 字节长度
// }

// 字符串是不可变的
// s[0] = 'h'  // ❌ 编译错误:cannot assign to s[0]

// 但可以整体替换
s = "Hello, World!"  // ✅ 整体赋值

6.2 字符串与Unicode

go 复制代码
// Go源码是UTF-8编码,字符串字面量也是
s := "你好,世界"

fmt.Println(len(s))                   // 18(UTF-8编码后的字节数)
fmt.Println(utf8.RuneCountInString(s)) // 5(Unicode字符数)

// 遍历字符串的两种方式
// 方式一:按字节遍历
for i := 0; i < len(s); i++ {
    fmt.Printf("%x ", s[i])
}
// 输出: e4 bd a0 e5 a5 bd ...

// 方式二:按Unicode字符遍历
for i, ch := range s {
    fmt.Printf("位置%d: %c ", i, ch)
}
// 输出: 位置0: 你 位置3: 好 位置6: , 位置9: 世 位置12: 界

6.3 字符串的字面量形式

go 复制代码
// 双引号字符串:支持转义
s1 := "Hello\nWorld\t!"
s2 := "他说:\"你好\""

// 反引号原始字符串:不支持转义,可以包含换行
s3 := `这是原始字符串
可以包含换行
\t不会被转义,会原样输出
`

// 多行字符串拼接
s4 := "第一部分" +
      "第二部分" +
      "第三部分"

// 常用于正则表达式和HTML模板
re := regexp.MustCompile(`\d+\.\d+\.\d+`)
template := `
<html>
    <body>
        <h1>Hello, {{.Name}}</h1>
    </body>
</html>
`

七、类型别名和类型定义

7.1 byte和rune

go 复制代码
// byte 是 uint8 的别名
var b byte = 65
fmt.Println(b)        // 65
fmt.Printf("%c\n", b) // A

// rune 是 int32 的别名
var r rune = '中'
fmt.Println(r)        // 20013
fmt.Printf("%c\n", r) // 中

7.2 类型定义创建新类型

go 复制代码
// 类型定义:创建一个全新的类型
type UserID int64
type UserName string
type Score float64

var uid UserID = 12345
var name UserName = "张三"

// 新类型不与原类型兼容
var i64 int64 = 12345
// uid = i64  // ❌ 编译错误
uid = UserID(i64)  // ✅ 需要显式转换

// 类型定义可以有自己方法,实现接口
type Celsius float64

func (c Celsius) String() string {
    return fmt.Sprintf("%.1f°C", c)
}

func (c Celsius) ToFahrenheit() float64 {
    return float64(c)*9/5 + 32
}

temp := Celsius(36.5)
fmt.Println(temp)              // 36.5°C
fmt.Println(temp.ToFahrenheit()) // 97.7

7.3 类型别名 vs 类型定义

go 复制代码
// 类型别名(Type Alias):完全等同
type MyInt = int  // MyInt 就是 int,完全一样

var a MyInt = 10
var b int = 20
a = b  // ✅ 可以直接赋值(它们是同一个类型)

// 类型定义(Type Definition):创建新类型
type YourInt int  // YourInt 是基于int的新类型

var c YourInt = 10
// var d int = c   // ❌ 编译错误:类型不匹配
var d int = int(c)  // ✅ 需要显式转换

八、类型选择决策树

复制代码
你需要存储什么内容?
├── 真/假值 → bool
├── 整数
│   ├── 普通计数/索引 → int
│   ├── 需要64位保证 → int64
│   ├── 需要节省内存(数组)→ int8/int16/int32
│   ├── 无符号值 → uint/uint8/uint16/uint32/uint64
│   ├── 单个字节 → byte (uint8)
│   └── Unicode字符 → rune (int32)
├── 小数
│   ├── 科学计算 → float64
│   ├── 图形/大数组 → float32
│   └── 金钱 → ❌ 用整数(分)
├── 文本
│   ├── 一般文本 → string
│   └── 可变字节 → []byte
└── 复数 → complex128

九、本篇总结

✅ 本篇我们全面了解了Go语言的基本数据类型:

  • 布尔型:严格的true/false,与整数不互通
  • 整数型:从int8到int64,int是默认选择
  • 浮点型:float64为默认,注意精度问题和金钱陷阱
  • 复数型:内建支持,用于科学计算
  • 字符串型:不可变,UTF-8编码,区分字节和字符
  • 类型系统:byte/rune别名,类型定义vs别名

💡 Go的基本类型虽然数量不多,但设计精良。记住几个核心原则:

  1. 默认整数用int,需要特定范围时用intX/uintX
  2. 金钱永远不用浮点数,用整数表示最小单位
  3. 用==比较浮点数前先想想精度问题
  4. 遍历字符串时注意是用range(按字符)还是索引(按字节)

下一篇,我们将深入探讨byte与rune的区别和使用场景,理解Go语言的字符处理机制。

相关推荐
alexhilton1 小时前
规范驱动开发:让AI生成符合预期的KMP代码
android·kotlin·android jetpack
码农coding2 小时前
android12 ActivityManagerService开机启动分析
android
何以解忧,唯有..2 小时前
Python os模块详解:文件与目录操作指南
java·服务器·python
Brilliantwxx2 小时前
【Linux】 进程(3)深度解析:从查看进程到进程状态
linux·服务器·网络·c++
我星期八休息2 小时前
Linux—五种IO模型与非阻塞IO
linux·运维·服务器·网络·数据库·网络协议
hweiyu002 小时前
Linux命令:column
linux·运维·服务器
绿蚁新亭3 小时前
Markdown Wiki 网页代码块复制至欧拉 Linux Shell 脚本隐形字符故障完整复盘
linux·运维·服务器
Loongproxy3 小时前
2026年动态IP代理真实可用率怎么横评?爬虫选代理的关键是什么
服务器·网络·网络协议·tcp/ip
mengge.cloud3 小时前
LVS 小白完整教程(Linux虚拟服务器负载均衡)
linux·运维·服务器·负载均衡·lvs