导航菜单

基本数据类型

类型概览

Go 是一门静态类型语言,每个变量在编译时都有明确的类型。Go 的基本数据类型可以分为以下几类:

  • 整数类型(intint8int16int32int64uint 系列)
  • 浮点类型(float32float64
  • 布尔类型(bool
  • 字符串类型(string
基本数据类型(Basic Data Types)

基本数据类型是编程语言内置的最基础的数据表示形式,是构建复合数据类型的基石。Go 中的基本类型又称为”值类型”,它们的值直接存储在变量中,赋值时会进行值拷贝。

整数类型

Go 提供了丰富的整数类型,分为有符号和无符号两大类:

类型大小范围
int81 字节(8 位)-128 ~ 127
int162 字节(16 位)-32768 ~ 32767
int324 字节(32 位)-2147483648 ~ 2147483647
int648 字节(64 位)-2^63 ~ 2^63-1
uint81 字节0 ~ 255
uint162 字节0 ~ 65535
uint324 字节0 ~ 2^32-1
uint648 字节0 ~ 2^64-1
int平台相关(32 或 64 位)32 位或 64 位平台上的范围
uint平台相关int 大小相同
uintptr足够存放指针平台相关
var a int = 42
var b int64 = 9223372036854775807
var c uint8 = 255
var d uintptr = 0x7ffe1234abcd

fmt.Printf("a: %T, %d\n", a, a)   // a: int, 42
fmt.Printf("b: %T, %d\n", b, b)   // b: int64, 9223372036854775807
fmt.Printf("c: %T, %d\n", c, c)   // c: uint8, 255
fmt.Printf("d: %T, %x\n", d, d)   // d: uintptr, 7ffe1234abcd

整数溢出

整数运算时,如果结果超出类型的表示范围,会发生溢出。Go 不会自动检测溢出,需要程序员自行注意:

var maxUint8 uint8 = 255
var result uint8 = maxUint8 + 1  // 溢出!结果为 0
fmt.Println(result)  // 0

var minInt8 int8 = -128
var result2 int8 = minInt8 - 1  // 溢出!结果为 127
fmt.Println(result2)  // 127

进制表示

Go 支持多种进制的整数字面量:

// 十进制
dec := 42

// 八进制(以 0 开头)
oct := 052  // 等于十进制 42

// 十六进制(以 0x 开头)
hex := 0x2A  // 等于十进制 42

// 二进制(以 0b 开头,Go 1.13+)
bin := 0b101010  // 等于十进制 42

fmt.Println(dec, oct, hex, bin)  // 42 42 42 42

Go 1.13+ 还支持使用 _ 作为数字分隔符,增强大数的可读性:

million := 1_000_000
maxInt64 := 9_223_372_036_854_775_807

浮点类型

Go 提供两种浮点类型:

类型大小精度
float324 字节约 6-7 位有效十进制数字
float648 字节约 15-16 位有效十进制数字
var f1 float32 = 3.14
var f2 float64 = 3.141592653589793

// math.MaxFloat64 是 float64 能表示的最大值
fmt.Println(math.MaxFloat64)  // 1.7976931348623157e+308

浮点数精度问题

浮点数采用 IEEE 754 标准存储,某些十进制小数无法精确表示,需要注意精度问题:

a := 0.1 + 0.2
fmt.Println(a)           // 0.30000000000000004
fmt.Println(a == 0.3)    // false!
func equal(a, b, epsilon float64) bool {
    return math.Abs(a-b) < epsilon
}

fmt.Println(equal(0.1+0.2, 0.3, 1e-9))  // true

布尔类型

bool 类型只有两个值:truefalse,零值为 false

布尔类型(Boolean)

布尔类型是表示逻辑真假的类型,只能取 truefalse 两个值,常用于条件判断。

var isActive bool       // 零值:false
var isValid := true     // 短声明

fmt.Println(isActive)   // false
fmt.Println(isValid)    // true

// 布尔运算
fmt.Println(true && false)  // false
fmt.Println(true || false)  // true
fmt.Println(!true)          // false
// 错误
// n := int(true)  // 编译错误

// 正确:使用条件表达式
var b bool = true
var n int
if b {
    n = 1
} else {
    n = 0
}

字符串类型

字符串(String)

字符串是 Go 中表示文本的基本类型。Go 中的字符串是不可变的字节序列(只读的字节 slice),底层使用 UTF-8 编码。

字符串基础

s1 := "Hello, World!"      // 双引号字符串,支持转义字符
s2 := `Hello\nWorld!`      // 反引号字符串(原始字符串),不解析转义字符
s3 := "你好" + "世界"       // 字符串拼接
s4 := "Hello"
s4 += " World"              // 字符串追加

fmt.Println(s1)  // Hello, World!
fmt.Println(s2)  // Hello\nWorld!(反斜杠和 n 都原样输出)
fmt.Println(s3)  // 你好世界
fmt.Println(s4)  // Hello World

常用字符串操作

s := "Hello, 世界"

// 长度(字节数,不是字符数)
fmt.Println(len(s))  // 13("Hello, "占7字节,"世"和"界"各占3字节)

// 字符串拼接
s1 := "Hello" + " " + "World"

// fmt.Sprintf 格式化
s2 := fmt.Sprintf("name: %s, age: %d", "Alice", 25)

// strings 包
fmt.Println(strings.Contains(s, "Hello"))   // true
fmt.Println(strings.HasPrefix(s, "Hello"))  // true
fmt.Println(strings.HasSuffix(s, "界"))     // true
fmt.Println(strings.Index(s, ","))          // 5
fmt.Println(strings.ToUpper(s))             // HELLO, 世界
fmt.Println(strings.Split("a,b,c", ","))   // [a b c]
fmt.Println(strings.TrimSpace("  hello  ")) // hello

// strings.Builder 高效拼接
var builder strings.Builder
for i := 0; i < 1000; i++ {
    builder.WriteString("x")
}
result := builder.String()

byte 与 rune

byte 与 rune
  • byteuint8 的别名,代表一个字节(8 位),用于表示 ASCII 字符。
  • runeint32 的别名,代表一个 Unicode 码点,用于表示 UTF-8 编码中的单个字符。
类型别名大小用途
byteuint81 字节ASCII 字符、原始字节
runeint324 字节Unicode 字符
// byte 用于 ASCII 字符
var ch byte = 'A'  // 65
fmt.Printf("%c %d\n", ch, ch)  // A 65

// rune 用于 Unicode 字符
var r rune = ''  // 19990
fmt.Printf("%c %d\n", r, r)    // 世 19990

UTF-8 编码

Go 的字符串底层使用 UTF-8 编码。UTF-8 是一种变长编码:

  • ASCII 字符(0-127):1 个字节
  • 大多数常用字符(包括中文):3 个字节
  • 其他字符:2 或 4 个字节
s := "Hello世界"

// len() 返回字节数
fmt.Println(len(s))  // 11(5 + 3 + 3)

// 使用 for range 遍历,按 rune 遍历
for i, r := range s {
    fmt.Printf("索引: %d, 字符: %c, rune值: %d\n", i, r, r)
}
// 输出:
// 索引: 0, 字符: H, rune值: 72
// 索引: 1, 字符: e, rune值: 101
// 索引: 2, 字符: l, rune值: 108
// 索引: 3, 字符: l, rune值: 108
// 索引: 4, 字符: o, rune值: 111
// 索引: 5, 字符: 世, rune值: 19990
// 索引: 8, 字符: 界, rune值: 30028
s := "Hello世界"
fmt.Println(len(s))                     // 11(字节数)
fmt.Println(utf8.RuneCountInString(s))  // 7(字符数)
fmt.Println(len([]rune(s)))             // 7(字符数)

// 将字符串转换为 rune 切片
runes := []rune(s)
fmt.Println(runes)      // [72 101 108 108 111 19990 30028]
fmt.Println(string(runes))  // Hello世界

类型转换

类型转换(Type Conversion)

Go 是强类型语言,不支持隐式类型转换。不同类型之间的转换必须使用显式的类型转换表达式 T(v)

数值类型之间的转换

var i int = 42
var f float64 = float64(i)       // int → float64
var u uint = uint(i)             // int → uint
var i32 int32 = int32(i)         // int → int32

// 浮点数 → 整数(截断小数部分)
var x float64 = 3.99
var y int = int(x)               // 3,不是四舍五入!
fmt.Println(y)  // 3

字符串与数值的转换

import (
    "fmt"
    "strconv"
)

// 字符串 → 整数
n, err := strconv.Atoi("42")              // n=42, err=nil
n2, err := strconv.ParseInt("42", 10, 64) // n2=int64(42)

// 整数 → 字符串
s := strconv.Itoa(42)                      // "42"
s2 := strconv.FormatInt(42, 10)            // "42"
s3 := fmt.Sprintf("%d", 42)                // "42"

// 字符串 → 浮点数
f, err := strconv.ParseFloat("3.14", 64)   // f=3.14

// 浮点数 → 字符串
s4 := strconv.FormatFloat(3.14, 'f', 2, 64) // "3.14"

// 字符串 → 布尔值
b, err := strconv.ParseBool("true")        // b=true

练习题

练习 1:类型转换与溢出

以下代码的输出是什么?请解释原因。

package main

import "fmt"

func main() {
    var x int64 = 300
    var y int8 = int8(x)
    fmt.Println(y)
}
参考答案

解题思路int64int8 时,如果值超出 int8 的范围(-128 ~ 127),会发生截断。

输出44

解释300 的二进制表示为 0000 0001 0010 1100。转换为 int8 时,只取低 8 位 0010 1100,即十进制的 44。这是典型的整数溢出截断行为。

练习 2:字符串与 rune

编写一个函数 reverseString,接收一个包含中文的字符串,返回其反转结果。例如输入 "Hello世界",输出 "界世olleH"

参考答案

解题思路:不能直接按字节反转(会导致 UTF-8 字符损坏),需要先将字符串转换为 []rune,再反转 rune 切片。

代码

package main

import "fmt"

func reverseString(s string) string {
    runes := []rune(s)
    for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
        runes[i], runes[j] = runes[j], runes[i]
    }
    return string(runes)
}

func main() {
    fmt.Println(reverseString("Hello世界"))  // 界世olleH
    fmt.Println(reverseString("你好世界"))    // 界世好你
    fmt.Println(reverseString("abc"))         // cba
}

关键点:将 string 转换为 []rune 可以正确处理 UTF-8 多字节字符,反转后再用 string() 转换回来。

练习 3:浮点数比较

编写一个函数 approximatelyEqual,判断两个 float64 值是否近似相等。要求支持相对误差比较。

参考答案

解题思路:使用相对误差和绝对误差的组合策略。当数值接近零时使用绝对误差,否则使用相对误差。

代码

package main

import (
    "fmt"
    "math"
)

func approximatelyEqual(a, b float64) bool {
    epsilon := 1e-9
    absA := math.Abs(a)
    absB := math.Abs(b)
    diff := math.Abs(a - b)

    // 当 a 或 b 接近零时,使用绝对误差
    if a == b {
        return true
    }
    if a == 0 || b == 0 || diff < math.SmallestNonzeroFloat64 {
        return diff < epsilon
    }
    // 否则使用相对误差
    return diff/math.Min(absA+absB, math.MaxFloat64) < epsilon
}

func main() {
    fmt.Println(approximatelyEqual(0.1+0.2, 0.3))     // true
    fmt.Println(approximatelyEqual(1.0, 1.000000001))  // true
    fmt.Println(approximatelyEqual(1.0, 2.0))          // false
}

搜索