# Go 基本数据类型

Go 是一门静态类型语言，每个变量都有确定的数据类型。理解 Go 的类型系统是编写正确、高效代码的前提。本篇将详细介绍 Go 中的整型、浮点型、布尔型、字符串、字符类型以及类型转换的方法。

![Go类型系统](https://img.zhaojq.top/20260729161335722.png "Go类型系统")

## 整型

Go 提供了丰富的整型类型，按照位数和是否有符号进行分类。

### 有符号整型

| 类型 | 位数 | 取值范围 |
|------|------|----------|
| `int8` | 8 | -128 ~ 127 |
| `int16` | 16 | -32768 ~ 32767 |
| `int32` | 32 | -2147483648 ~ 2147483647 |
| `int64` | 64 | -9223372036854775808 ~ 9223372036854775807 |
| `int` | 平台相关（32位或64位） | 取决于平台 |

### 无符号整型

| 类型 | 位数 | 取值范围 |
|------|------|----------|
| `uint8` | 8 | 0 ~ 255 |
| `uint16` | 16 | 0 ~ 65535 |
| `uint32` | 32 | 0 ~ 4294967295 |
| `uint64` | 64 | 0 ~ 18446744073709551615 |
| `uint` | 平台相关 | 取决于平台 |

### uintptr

`uintptr` 是一个特殊的整型，其大小足以容纳任何指针值。它主要用于底层编程和 unsafe 操作：

```go
package main

import (
    "fmt"
    "unsafe"
)

func main() {
    x := 42
    p := &x
    addr := uintptr(unsafe.Pointer(p))
    fmt.Printf("变量 x 的内存地址: %d (十六进制: %x)\n", addr, addr)
}
```

### 整型使用示例

```go
package main

import (
    "fmt"
    "math"
)

func main() {
    var a int = 100
    var b int8 = 127
    var c int64 = math.MaxInt64
    var d uint = 200

    fmt.Printf("int:   %d (占 %d 字节)\n", a, unsafe.Sizeof(a))
    fmt.Printf("int8:  %d\n", b)
    fmt.Printf("int64: %d\n", c)
    fmt.Printf("uint:  %d\n", d)
}
```

> **注意**：日常开发中，一般使用 `int` 即可，除非有特殊的存储或协议需求。

## 浮点型

Go 提供两种浮点数类型：

| 类型 | 位数 | 说明 |
|------|------|------|
| `float32` | 32 | 单精度浮点数，约 7 位有效数字 |
| `float64` | 64 | 双精度浮点数，约 15 位有效数字 |

### 基本使用

```go
package main

import "fmt"

func main() {
    var pi32 float32 = 3.14159265358979323846
    var pi64 float64 = 3.14159265358979323846

    fmt.Printf("float32: %.15f\n", pi32) // 精度丢失
    fmt.Printf("float64: %.15f\n", pi64) // 更精确
}
```

### 精度问题

浮点数运算存在精度问题，这在所有编程语言中都是如此：

```go
package main

import "fmt"

func main() {
    a := 0.1
    b := 0.2
    c := a + b

    fmt.Println(c)           // 0.30000000000000004
    fmt.Println(c == 0.3)    // false

    // 正确做法：使用误差范围比较
    const epsilon = 1e-9
    if c-0.3 < epsilon && c-0.3 > -epsilon {
        fmt.Println("a + b 约等于 0.3")
    }
}
```

> **建议**：涉及金额计算时，使用整数（以分为单位）或 `math/big` 包来避免精度问题。

## 布尔型

Go 的布尔类型只有两个值：`true` 和 `false`。

```go
package main

import "fmt"

func main() {
    var isActive bool       // 零值为 false
    isReady := true

    fmt.Println("isActive:", isActive) // false
    fmt.Println("isReady:", isReady)   // true

    // 布尔值不能与整数互相转换
    // var n int = 1
    // isActive = bool(n) // 编译错误！
    _ = isReady
}
```

> **注意**：Go 中布尔值不能像 C 语言那样用 0/1 替代，也不能隐式转换为整数。

## 字符串

字符串是 Go 中最常用的数据类型之一。Go 中的字符串是**不可变的字节序列**。

### 基本操作

```go
package main

import "fmt"

func main() {
    // 双引号声明（支持转义字符）
    s1 := "Hello, Go!"

    // 反引号声明（原始字符串，不支持转义，支持换行）
    s2 := `这是一个
多行字符串
不需要转义 \n`

    fmt.Println(s1)
    fmt.Println(s2)

    // 字符串长度
    fmt.Println("长度:", len(s1)) // 11

    // 字符串拼接
    greeting := "Hello" + ", " + "World!"
    fmt.Println(greeting)
}
```

### 字节 vs Rune

字符串底层是字节（byte）序列。访问字符串中的单个字符时需要注意中文等多字节字符：

```go
package main

import "fmt"

func main() {
    s := "Hello世界"

    // 按字节访问 —— 中文会被拆成多个字节
    fmt.Println("按字节遍历:")
    for i := 0; i < len(s); i++ {
        fmt.Printf("%c (%d) ", s[i], s[i])
    }
    fmt.Println()

    // 按 rune 访问 —— 正确处理 Unicode 字符
    fmt.Println("按 rune 遍历:")
    for _, r := range s {
        fmt.Printf("%c (%d) ", r, r)
    }
    fmt.Println()

    // 字节长度 vs 字符数
    fmt.Println("字节长度:", len(s))           // 13
    fmt.Println("字符数量:", len([]rune(s)))   // 7
}
```

### 常用字符串操作

```go
package main

import (
    "fmt"
    "strings"
)

func main() {
    s := "  Hello, Go World!  "

    // 去除首尾空白
    trimmed := strings.TrimSpace(s)
    fmt.Println(trimmed) // "Hello, Go World!"

    // 查找子串
    fmt.Println(strings.Contains(trimmed, "Go"))    // true
    fmt.Println(strings.Index(trimmed, "World"))     // 10

    // 替换
    replaced := strings.Replace(trimmed, "Go", "Golang", 1)
    fmt.Println(replaced) // "Hello, Golang World!"

    // 分割
    parts := strings.Split("a,b,c,d", ",")
    fmt.Println(parts) // [a b c d]

    // 拼接
    joined := strings.Join(parts, "-")
    fmt.Println(joined) // "a-b-c-d"

    // 大小写转换
    fmt.Println(strings.ToUpper("hello")) // HELLO
    fmt.Println(strings.ToLower("WORLD")) // world

    // 格式化字符串
    name := "张三"
    age := 25
    info := fmt.Sprintf("姓名: %s, 年龄: %d", name, age)
    fmt.Println(info)
}
```

## 字符类型

Go 中没有专门的字符类型，而是用以下两种方式表示字符：

### byte（等同于 uint8）

`byte` 表示 ASCII 字符或 UTF-8 编码中的一个字节：

```go
package main

import "fmt"

func main() {
    var b byte = 'A'
    fmt.Printf("字符: %c, ASCII 码: %d\n", b, b)
    fmt.Printf("byte 的底层类型是 uint8: %T\n", b)
}
```

### rune（等同于 int32）

`rune` 表示一个 Unicode 码点，可以表示任何语言的字符：

```go
package main

import "fmt"

func main() {
    var r rune = '中'
    fmt.Printf("字符: %c, Unicode: %U\n", r, r)
    fmt.Printf("rune 的底层类型是 int32: %T\n", r)

    // 各种语言的字符
    chars := []rune{'你', '好', '世', '界', '🌍'}
    for _, c := range chars {
        fmt.Printf("%c ", c)
    }
    fmt.Println()
}
```

### byte 与 rune 的选择

- 处理 ASCII 文本或二进制数据时，使用 `byte`。
- 处理包含中文等非 ASCII 字符的文本时，使用 `rune`。

## 类型转换

Go 是强类型语言，**不支持隐式类型转换**，必须显式转换。

### 基本类型转换

```go
package main

import "fmt"

func main() {
    // 整型之间的转换
    var a int = 100
    var b int64 = int64(a)
    var c float64 = float64(a)
    var d int = int(3.99) // 截断小数部分，结果为 3

    fmt.Printf("int -> int64: %d (%T)\n", b, b)
    fmt.Printf("int -> float64: %f (%T)\n", c, c)
    fmt.Printf("float64 -> int: %d (%T)\n", d, d)
}
```

### 使用 strconv 包进行字符串转换

```go
package main

import (
    "fmt"
    "strconv"
)

func main() {
    // 整数转字符串
    s1 := strconv.Itoa(42)       // int to ascii
    fmt.Printf("Itoa: %q\n", s1) // "42"

    // 字符串转整数
    n, err := strconv.Atoi("123")
    if err != nil {
        fmt.Println("转换错误:", err)
    }
    fmt.Printf("Atoi: %d\n", n)

    // 其他进制转换
    s2 := strconv.FormatInt(255, 16) // 十进制转十六进制
    fmt.Println("255 的十六进制:", s2) // "ff"

    n2, _ := strconv.ParseInt("ff", 16, 64) // 十六进制转十进制
    fmt.Println("0xff 的十进制:", n2) // 255

    // 浮点数与字符串互转
    s3 := strconv.FormatFloat(3.14, 'f', 2, 64)
    fmt.Println("浮点数转字符串:", s3) // "3.14"

    f, _ := strconv.ParseFloat("2.718", 64)
    fmt.Println("字符串转浮点数:", f) // 2.718

    // 布尔值与字符串互转
    s4 := strconv.FormatBool(true)
    fmt.Println("布尔转字符串:", s4) // "true"

    b, _ := strconv.ParseBool("false")
    fmt.Println("字符串转布尔:", b) // false
}
```

## 零值总结表

| 类型 | 零值 | 示例 |
|------|------|------|
| `int`, `int8`, `int16`, `int32`, `int64` | `0` | `var n int` → `0` |
| `uint`, `uint8`, `uint16`, `uint32`, `uint64` | `0` | `var u uint` → `0` |
| `uintptr` | `0` | `var p uintptr` → `0` |
| `float32`, `float64` | `0.0` | `var f float64` → `0` |
| `bool` | `false` | `var b bool` → `false` |
| `string` | `""` | `var s string` → `""` |
| `byte` (uint8) | `0` | `var b byte` → `0` |
| `rune` (int32) | `0` | `var r rune` → `0` |
| 指针、函数、接口、slice、channel、map | `nil` | `var p *int` → `nil` |

## 总结

本篇详细介绍了 Go 的基本数据类型：整型（`int` 系列和 `uint` 系列）、浮点型（`float32`/`float64`）、布尔型（`bool`）、字符串（`string`）以及字符表示方式（`byte` 和 `rune`）。我们了解了浮点数精度问题的本质及应对方法，掌握了字符串的字节与 rune 遍历方式，以及使用 `strconv` 包进行类型转换的技巧。Go 的强类型系统要求显式类型转换，虽然多写了几行代码，但大大减少了运行时的类型错误。

