Day02(数据类型、变量与常量)
Go语言数据类型详解、变量声明与初始化、常量与iota枚举
概述
Go 是一门静态类型语言,每个变量都有明确的类型。本文详细介绍 Go 的数据类型系统、变量声明方式和常量定义。
目录
1. 整型(Integer Types)
整型分为有符号和无符号两大类,区别在于如何解释二进制数据。
有符号整型 vs 无符号整型
| 特性 | 有符号整型 (Signed) | 无符号整型 (Unsigned) |
|---|---|---|
| 表示范围 | 负数、零、正数 | 零、正数 |
| 最高位用途 | 符号位(0=正,1=负) | 数值位 |
| 相同位数范围 | 较小(需表示负数) | 较大(全用于正数) |
| 应用场景 | 通用计算、温度、坐标 | 计数、索引、位运算 |
整型类型表
| 类型 | 位数 | 最小值 | 最大值 | 说明 |
|---|---|---|---|---|
int8 |
8 | -128 | 127 | 小范围整数 |
int16 |
16 | -32,768 | 32,767 | 中等范围 |
int32 / rune |
32 | -2,147,483,648 | 2,147,483,647 | Unicode 字符 |
int64 |
64 | -9.2×10¹⁸ | 9.2×10¹⁸ | 大范围整数 |
uint8 / byte |
8 | 0 | 255 | 字节数据 |
uint16 |
16 | 0 | 65,535 | 无符号中等范围 |
uint32 |
32 | 0 | 4,294,967,295 | 无符号大范围 |
uint64 |
64 | 0 | 1.8×10¹⁹ | 无符号超大范围 |
int |
平台相关 | 依赖架构 | 依赖架构 | 32位或64位 |
uint |
平台相关 | 0 | 依赖架构 | 32位或64位 |
uintptr |
平台相关 | - | - | 指针整型 |
注意:
int和uint的大小取决于平台:
- 32位系统:4字节(32位)
- 64位系统:8字节(64位)
实际示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main
import "fmt"
func main() {
// 声明不同整型
var age int8 = 25
var population int64 = 7800000000
var byteData byte = 255
var runeChar rune = '中'
fmt.Printf("age: %d (type: %T)\n", age, age)
fmt.Printf("population: %d (type: %T)\n", population, population)
fmt.Printf("byteData: %d (type: %T)\n", byteData, byteData)
fmt.Printf("runeChar: %c (type: %T, code: %d)\n", runeChar, runeChar, runeChar)
}
输出:
1
2
3
4
age: 25 (type: int8)
population: 7800000000 (type: int64)
byteData: 255 (type: uint8)
runeChar: 中 (type: int32, code: 20013)
2. 浮点型(Floating-Point Types)
Go 支持 IEEE 754 标准的浮点数,分为 float32 和 float64。
浮点型对比
| 类型 | 位数 | 精度 | 最小值 | 最大值 | 适用场景 |
|---|---|---|---|---|---|
float32 |
32 | ~6-7位有效数字 | ±1.4×10⁻⁴⁵ | ±3.4×10³⁸ | 图形处理、科学计算(节省内存) |
float64 |
64 | ~15-16位有效数字 | ±4.9×10⁻³²⁴ | ±1.8×10³⁰⁸ | 推荐默认使用,金融计算 |
浮点数注意事项
⚠️ 重要警告: 浮点数存在精度问题,不适合精确计算!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main
import "fmt"
func main() {
// 浮点数精度问题
a := 0.1
b := 0.2
c := a + b
fmt.Printf("0.1 + 0.2 = %.20f\n", c)
fmt.Printf("c == 0.3: %v\n", c == 0.3) // false!
// 正确做法:使用误差范围比较
epsilon := 1e-9
isEqual := (c - 0.3) < epsilon && (c - 0.3) > -epsilon
fmt.Printf("Approximately equal: %v\n", isEqual) // true
}
输出:
1
2
3
0.1 + 0.2 = 0.30000000000000004441
c == 0.3: false
Approximately equal: true
建议:
- 一般场景使用
float64- 金融计算使用
decimal库(如github.com/shopspring/decimal)- 避免直接比较浮点数相等
3. 布尔型(Boolean Type)
布尔型只有两个值:true 和 false,默认值为 false。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main
import "fmt"
func main() {
var isActive bool // 默认 false
var isCompleted bool = true
// 布尔运算
fmt.Println(isActive && isCompleted) // AND: false
fmt.Println(isActive || isCompleted) // OR: true
fmt.Println(!isActive) // NOT: true
// 常见用法
age := 20
isAdult := age >= 18
fmt.Printf("Is adult: %v\n", isAdult) // true
}
4. 字符串(String Type)
字符串是不可变的 UTF-8 编码字节序列。
字符串特性
- 不可变性: 创建后不能修改
- UTF-8 编码: 原生支持 Unicode
- 双引号:
"Hello"- 支持转义字符 - 反引号:
`Hello`- 原始字符串,不转义
字符串操作示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package main
import (
"fmt"
"strings"
)
func main() {
// 字符串声明
s1 := "Hello, World!"
s2 := `Raw string
with newline
and \t tab`
fmt.Println(s1)
fmt.Println(s2)
// 常用操作
fmt.Println(len(s1)) // 长度: 13
fmt.Println(s1[0]) // 字节: 72 ('H')
fmt.Println(s1[7:]) // 切片: "World!"
fmt.Println(strings.ToUpper(s1)) // 大写: "HELLO, WORLD!"
fmt.Println(strings.Contains(s1, "World")) // true
// 字符串拼接
s3 := s1 + " Welcome"
fmt.Println(s3)
// 遍历字符串(按 rune)
for i, char := range "你好世界" {
fmt.Printf("Index: %d, Char: %c, Bytes: %d\n", i, char, len(string(char)))
}
}
输出:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Hello, World!
Raw string
with newline
and \t tab
13
72
World!
HELLO, WORLD!
true
Hello, World! Welcome
Index: 0, Char: 你, Bytes: 3
Index: 3, Char: 好, Bytes: 3
Index: 6, Char: 世, Bytes: 3
Index: 9, Char: 界, Bytes: 3
byte vs rune
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main
import "fmt"
func main() {
s := "Hello 世界"
// 按 byte 遍历
fmt.Println("Bytes:")
for i := 0; i < len(s); i++ {
fmt.Printf("%d ", s[i])
}
fmt.Println()
// 按 rune 遍历
fmt.Println("\nRunes:")
for _, r := range s {
fmt.Printf("%c ", r)
}
fmt.Println()
}
关键点:
byte=uint8: 处理 ASCII 或二进制数据rune=int32: 处理 Unicode 字符(中文、emoji 等)
复合数据类型
5. 其他基本类型
复数类型(Complex Types)
Go 原生支持复数运算:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main
import "fmt"
func main() {
var c1 complex64 = 3 + 4i // float32 实部和虚部
var c2 complex128 = 5 + 6i // float64 实部和虚部
fmt.Printf("c1: %v, real: %f, imag: %f\n", c1, real(c1), imag(c1))
fmt.Printf("c2: %v, real: %f, imag: %f\n", c2, real(c2), imag(c2))
// 复数运算
sum := c1 + c2
fmt.Printf("Sum: %v\n", sum)
}
6. 复合类型概览
| 类型 | 说明 | 示例 |
|---|---|---|
| 数组 (Array) | 固定长度序列 | [5]int{1,2,3,4,5} |
| 切片 (Slice) | 动态数组 | []int{1,2,3} |
| 映射 (Map) | 键值对集合 | map[string]int{"a": 1} |
| 通道 (Channel) | 协程通信 | make(chan int) |
| 结构体 (Struct) | 自定义类型 | type User struct {...} |
| 接口 (Interface) | 方法集合 | type Reader interface {...} |
| 指针 (Pointer) | 内存地址 | *int |
| 函数 (Function) | 一等公民 | func(int) int |
这些类型将在后续章节详细讲解
变量声明与初始化
1. 变量声明方式
Go 提供了多种变量声明方式,各有适用场景。
方式一:var 声明(标准方式)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main
import "fmt"
func main() {
// 单个变量声明
var name string
var age int
var isActive bool
fmt.Printf("name: '%s', age: %d, isActive: %v\n", name, age, isActive)
// 输出: name: '', age: 0, isActive: false (零值)
// 批量声明
var (
host = "localhost"
port = 8080
path = "/api"
)
fmt.Printf("%s:%d%s\n", host, port, path)
}
方式二:短变量声明 := (推荐在函数内使用)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main
import "fmt"
func main() {
// 自动推断类型
name := "Alice"
age := 25
isActive := true
fmt.Printf("%T: %v\n", name, name) // string: Alice
fmt.Printf("%T: %v\n", age, age) // int: 25
fmt.Printf("%T: %v\n", isActive, isActive) // bool: true
// 多变量声明
x, y, z := 1, 2.5, "hello"
fmt.Println(x, y, z)
}
注意:
:=只能在函数内部使用,且至少有一个新变量
方式三:带初始化的 var 声明
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main
import "fmt"
func main() {
// 声明并初始化
var name string = "Bob"
var age int = 30
// 类型推断
var city = "Beijing" // 自动推断为 string
fmt.Println(name, age, city)
}
2. 零值(Zero Values)
未显式初始化的变量会被赋予类型的零值:
| 类型 | 零值 |
|---|---|
整型 (int, int8, etc.) |
0 |
浮点型 (float32, float64) |
0.0 |
布尔型 (bool) |
false |
字符串 (string) |
"" (空字符串) |
| 指针、切片、Map、Channel、函数、接口 | nil |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main
import "fmt"
func main() {
var i int
var f float64
var b bool
var s string
var ptr *int
fmt.Printf("int: %d\n", i) // 0
fmt.Printf("float64: %f\n", f) // 0.000000
fmt.Printf("bool: %v\n", b) // false
fmt.Printf("string: '%s'\n", s) // ''
fmt.Printf("pointer: %v\n", ptr) // <nil>
}
iota
iota是一个预定义的标识符,用于创建自增的无类型整数常量。它通常用于枚举类型,为每个枚举值生成一个唯一的常量。iota的行为类似于一个计数器,每次在常量声明中出现时都会自增一次。
基本用法
iota在每个const关键字出现时被重置为 0,并在每个const块中为每个新声明的常量自增。如果在同一行声明了多个常量,则它们将共享相同的iota值,除非显式地给其中一个常量赋值。
使用示例
以下是一个使用iota来定义星期几的枚举类型的示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main
import "fmt"
func main() {
const (
Sunday = iota // iota 初始化为 0
Monday // iota 自增为 1
Tuesday // iota 自增为 2
Wednesday // iota 自增为 3
Thursday // iota 自增为 4
Friday // iota 自增为 5
Saturday // iota 自增为 6
)
fmt.Println(Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday) // 输出: 0 1 2 3 4 5 6
}
在这个例子中,iota从 0 开始,并在每个新的常量声明中自动递增。因此,Sunday的值为 0,Monday的值为 1,依此类推。
递增规则
iota从 0 开始递增。- 每个
const块中的iota值都会自动递增,不需要显式操作。 - 在同一个
const块中,每次遇到iota都会自增。 - 在不同的
const块中,iota会重新从 0 开始递增。
通过使用iota,可以简洁地定义一系列有序的常量,提高代码的可读性和可维护性。
特殊例子
展示
iota的特殊情况,包括在同一行声明多个常量时如何共享相同的iota值,以及如何显式地给某个常量赋值以改变iota的自动递增。
特殊情况示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package main
import "fmt"
func main() {
const (
// iota 在这里初始化为 0
Sunday = iota // iota 值为 0
Monday // iota 值为 1
Tuesday // iota 值为 2
// 显式赋值会改变 iota 的值
WeekdayStart = Monday // WeekdayStart 值为 1,但 iota 仍然为 2
// 同一行声明的常量共享相同的 iota 值
Wednesday, Thursday = iota + 2, iota + 3 // Wednesday 值为 4,Thursday 值为 5
// iota 继续自增
Friday // iota 值为 6
Saturday // iota 值为 7
// 可以在新的 const 块中重置 iota
const (
firstQuarter = iota // iota 被重置为 0
secondQuarter // iota 值为 1
)
)
fmt.Println(Sunday, Monday, Tuesday, WeekdayStart) // 输出: 0 1 2 1
fmt.Println(Wednesday, Thursday, Friday, Saturday) // 输出: 4 5 6 7
fmt.Println(firstQuarter, secondQuarter) // 输出: 0 1
}
在这个例子中,首先定义了星期几的常量,并使用iota进行自动递增。然后,我们显式地将Monday的值赋给了WeekdayStart,但请注意,这并不会改变iota的自动递增行为,iota的值在赋值之后继续递增。
接下来,在同一行声明了Wednesday和Thursday,并为它们分别赋了iota + 2和iota + 3的值。由于它们在同一行声明,所以它们都使用了同一个iota值(此时为 2),但加上了不同的偏移量。
最后,在一个新的const块中再次使用了iota,它会被重置为 0,并在该块中继续递增。
通过这个示例,可以看到iota的灵活性和在定义枚举或常量序列时的强大功能。
常量
在 Go 语言中,常量(Constants)是固定不变的值,它们在程序编译时被确定,并且在整个程序的执行过程中都不能被修改。常量可以是布尔值、数字(整数或浮点数)、字符串或枚举值。
常量的声明和初始化
在 Go 语言中,常量可以使用const关键字进行声明。你可以在同一行声明多个常量,如果它们具有相同的类型。如果常量的类型没有明确指定,那么 Go 编译器会根据常量的值来推断其类型。
以下是一些常量声明的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package main
import "fmt"
func main() {
// 声明并初始化单个常量
const Pi = 3.14159
// 在同一行声明多个相同类型的常量
const (
StatusOK = 200
StatusFound = 302
StatusBad = 400
)
// 使用 iota 进行递增的常量声明
const (
x = iota // x == 0
y // y == 1
z // z == 2
wp // 如果在这一行前没有显式赋值,则 wp 也会自增,wp == 3
)
// 显式赋值会改变 iota 的自增行为
const (
v = iota // v == 0
u = 100 // u == 100, iota 的自增被这个显式赋值打断了
vv = iota // vv == 1, iota 继续从上一个显式值开始自增
ww = "hello" // ww 是字符串类型,iota 的自增被打断
xx // xx 在这里会是一个新的 iota 值,但它不会被使用(因为没有赋值给它)
)
fmt.Println(Pi)
fmt.Println(StatusOK, StatusFound, StatusBad)
fmt.Println(x, y, z, wp)
// 注意:vv 和 ww 不会被打印,因为它们没有在这段代码中被使用
}
常量的类型
- 数值常量:可以是整数或浮点数。
- 布尔常量:可以是
true或false。 - 字符串常量:用双引号(
")或反引号(```)括起来的字符序列。反引号中的字符串会原样输出,不进行转义。
常量的特点
- 常量在声明时必须赋值。
- 常量一旦被声明,其值就不能再改变。
- 常量在编译时就已经确定,因此它们的值在运行时不会改变。
- 常量可以是任何基本数据类型,也可以是数组、结构体和切片的组合类型,但不能是函数、映射或通道类型。
iota 枚举
iota是 Go 语言中的一个特殊常量,用于创建枚举值。在const声明中,iota会被自动赋值为 0,然后在每次新的const声明中自增 1。iota通常用于生成一组递增的整数常量。如上面示例中所示,你可以在同一行使用iota为多个常量赋值,或者在不同的行中使用iota来生成连续的整数常量。