基础入门
约 2455 字大约 8 分钟
2025-04-17
Hello World
package main
import "fmt"
func main() {
fmt.Println("hello, world")
}Go 程序结构
每个 Go 程序由以下部分组成:
package— 包声明,main包是可执行程序的入口import— 导入依赖包func main()— 程序执行的入口函数
变量声明
Go 有三种声明变量的方式:
// 1. var 声明(默认为零值)
var a int // 0
var name string // ""
var flag bool // false
// 2. var 声明并初始化
var weight = 23.4
var pi float64 = 3.14
// 3. 短声明 := (只能在函数内使用)
age := 18
count := 5注
短声明 := 不能在 package 级别(函数外)使用,但可以在函数内的任何位置(包括 for/if/switch 内外)使用
多变量声明
// 方式一:分组声明
var (
one = 1
two = 2
)
// 方式二:同行声明
var one, two = 1, 2
// 方式三:短声明(最常用)
one, two := 1, 2零值(初始值)
Go 中变量声明后会自动赋予「零值」,不会出现未初始化变量:
var a int // 0
var b bool // false
var c float32 // 0
var d string // ""(空字符串)常量
// 常量定义时必须赋值,之后不可修改
const pi = 3.14
// 批量声明常量
const (
num1 = 13
num2 = 15
)注意
Go 中的常量使用 const 关键字,一旦赋值就不能修改。
基本数据类型
| 类型 | 说明 | 取值范围 |
|---|---|---|
int / uint | 平台相关(32/64位) | 32位或64位 |
int8 ~ int64 | 有符号整数 | -128~127 到 -263~263-1 |
uint8 ~ uint64 | 无符号整数 | 0~255 到 0~2^64-1 |
float32 / float64 | 浮点数 | IEEE-754 |
byte | uint8 别名 | 0~255 |
rune | int32 别名,表示 Unicode 码点 | Unicode 字符 |
string | 字符串 | — |
bool | 布尔值 | true / false |
整数环绕
var num1 uint8 = 255
num1++ // 绕回 0(uint8 范围 0~255)
var num2 int8 = 127
num2++ // 绕回 -128(int8 范围 -128~127)byte 与 rune
var a byte = 'a'
fmt.Printf("%c 对应的 ASCII 值为 %d\n", a, a) // a 97
var z rune = '庞'
fmt.Printf("%c 对应的 Unicode 值为 %d\n", z, z) // 庞 24247提示
用 byte 表示 ASCII 字符,rune 表示 Unicode 字符(含中文)。
统计字符串中字符数量时,len() 返回的是字节数,要统计真正的字符数需转为 []rune:
s := "今天happy"
fmt.Println(len(s)) // 输出 11(字节数)
fmt.Println(len([]rune(s))) // 输出 7(字符数)类型别名
type myInt = int
var a myInt = 8
fmt.Printf("a 是 %T 类型,值 = %v\n", a, a) // int, 8ASCII 与字符打印
var (
pi rune = 960
alpha rune = 940
bang byte = 33
)
fmt.Printf("%c %c %c\n", pi, alpha, bang) // π ά !运算符
Go 的运算符按优先级从高到低排列(同一行优先级相同):
| 优先级 | 运算符 | 说明 |
|---|---|---|
| 5 | * / % << >> & &^ | 乘除、取模、位运算 |
| 4 | + - ` | ^` |
| 3 | == != < <= > >= | 比较运算符 |
| 2 | && | 逻辑与 |
| 1 | || | 逻辑或 |
注
更高优先级的运算符先被求值。同优先级按左结合规则运算。
自增 / 自减
++ 和 -- 在 Go 中是语句而不是表达式,因此没有前缀形式,也不能写在表达式中:
// ✅ 正确:作为独立语句
i++
i--
// ❌ 错误:不能作为表达式
// j = i++ // 编译错误
// ++i // 编译错误
// for i := 0; i < 5; ++i {} // 编译错误赋值运算符
a += 2 // a = a + 2
a -= 2 // a = a - 2
a *= 2 // a = a * 2
a /= 2 // a = a / 2
a %= 2 // a = a % 2
a &= 2 // a = a & 2
a |= 2 // a = a | 2
a ^= 2 // a = a ^ 2
a <<= 2 // a = a << 2
a >>= 2 // a = a >> 2字面量
整数字面量
n := 42 // 十进制
n := 0b101010 // 二进制(0b 或 0B 前缀)
n := 0o52 // 八进制(0o 或 0O 前缀)
n := 0x2A // 十六进制(0x 或 0X 前缀)
n := 1_000 // 数字分隔符(_ 提升可读性)
n := 0xdead_beef // 十六进制中使用分隔符字符串字面量
Go 支持两种字符串字面量:
// 双引号字符串 — 支持转义字符
s := "hello\nworld\"!" // 含换行和引号
// 反引号原始字符串 — 所见即所得,不处理转义
s := `hello\nworld` // 输出:hello\nworld(反斜杠是字面字符)
s := `多行
字符串
内容` // 可跨行,保留换行和空格字符转义表
| 转义序列 | 含义 |
|---|---|
\n | 换行符(LF) |
\r | 回车符(CR) |
\t | 制表符(Tab) |
\\ | 反斜杠 |
\" | 双引号 |
\a | 响铃(Bell) |
\b | 退格(Backspace) |
\f | 换页(Form Feed) |
\v | 垂直制表符 |
fmt.Println("Hello\tWorld") // Hello World(中间有 Tab)
fmt.Println("Line1\nLine2") // 换行输出
fmt.Println("Path: C:\\Go") // Path: C:\Go类型转换
Go 没有隐式类型转换,不同类型必须显式转换:
var i int = 42
var f float64 = float64(i) // int → float64
var u uint = uint(f) // float64 → uint
// 不同类型不能直接运算
var a int = 3
var b int64 = 5
// fmt.Println(a + b) // ❌ 编译错误:mismatched types int and int64
fmt.Println(a + int(b)) // ✅ 必须显式转换
// float → int 截断小数部分
var pi float64 = 3.14159
fmt.Println(int(pi)) // 3
// int → string 会解释为 Unicode 码点
fmt.Println(string(65)) // "A"(ASCII 65 → 'A')
fmt.Println(string(24247)) // "庞"
// string → []byte / []rune
s := "Hi"
fmt.Println([]byte(s)) // [72 105]
fmt.Println([]rune(s)) // [72 105]提示
类型转换的语法 T(v) 表示将值 v 转换为类型 T。转换是安全的,但可能丢失精度(如 float64 → int 截断小数)。
iota
iota 是常量声明中自动递增的计数器,每遇到一个 const 块就重置为 0:
const (
a = iota // 0
b // 1(继承 iota)
c // 2
)
const (
North = iota // 0(新 const 块,重新从 0 开始)
East // 1
South // 2
West // 3
)跳过值
使用 _ 跳过不需要的值:
const (
_ = iota // 跳过 0
KB = 1 << (10 * iota) // 1 << 10 = 1024
MB // 1 << 20
GB // 1 << 30
)位掩码场景
const (
Read = 1 << iota // 1
Write // 2
Execute // 4
)提示
iota 在每次遇到 const 关键字时重置,在同一 const 块内每行递增 1。
关键字与标识符
25 个关键字
Go 中共有 25 个关键字,分为四类:
| 类别 | 关键字 |
|---|---|
| 程序声明 | package import |
| 实体声明 | const var func type struct interface map chan |
| 控制流 | if else switch case default for range break continue return fallthrough go defer select |
| 其他 | goto |
注
true、false、iota、nil、make、len、new、append 等是预定义标识符,不是关键字。
标识符命名规则
- 由字母、数字、下划线组成
- 不能以数字开头
- 区分大小写(
Name≠name≠NAME)
name := "Alice" // ✅ 合法
_name := "Bob" // ✅ 合法(以下划线开头)
Name := "Carol" // ✅ 合法(大写字母开头)
// 123abc := 1 // ❌ 编译错误:不能以数字开头
// my-var := 1 // ❌ 编译错误:不能包含连字符可见性规则
标识符的可见性由首字母大小写决定:
var Name string // ✅ 首字母大写 — 导出(public),包外可见
var name string // ✅ 首字母小写 — 未导出(private),仅包内可见| 首字母 | 可见性 |
|---|---|
| 大写 | 导出(public),跨包访问 |
| 小写 | 未导出(private),仅本包内访问 |
格式化输出
| 格式化动词 | 说明 |
|---|---|
%v | 值的默认格式 |
%#v | 值的 Go 语法表示(含引号、结构体字段名等) |
%T | 值的类型 |
%c | 字符(Unicode 码点) |
%d | 十进制整数 |
%x | 十六进制 |
%f | 浮点数 |
%s | 字符串 |
fmt.Printf("%T\n", "hello") // string
fmt.Printf("%#v\n", "") // ""(空字符串的完整表示)
// 将格式化后的内容赋值给变量
var op = fmt.Sprintf("%d", num1)宽度与填充
// 正数向右填充空格,负数向左填充
fmt.Printf("%-4vhello%4v\n", 12, pi)
// 输出: "12 hello3.14"
// 用 0 填充,指定宽度
fmt.Printf("color: #%02x%02x%02x\n", red, green, blue)
// 输出: color: #008dd5输入输出
var name string
fmt.Print("请输入你的名字:")
fmt.Scan(&name)
fmt.Println("你的名字是:", name)
// 带错误处理
var age int
_, err := fmt.Scan(&age)
fmt.Println(age, err)命令行参数
import "os"
func main() {
var s, sep string
for i := 1; i < len(os.Args); i++ {
s += sep + os.Args[i]
sep = " "
}
fmt.Println(s)
}控制流
if-else
if v := math.Pow(3, 2); v < 10 {
return v
} else {
fmt.Printf("%g >= 10\n", v)
}
if后可以像for一样先执行一个简单语句,再判断条件。
for 循环
Go 只有 for 循环,没有 while 或 do-while:
// 标准 for 循环
for i := 0; i < 5; i++ {
fmt.Println(i)
}
// 类似 while
for i < 10 {
i++
}
// 无限循环
for {
// ...
break
}switch
// 1. switch 表达式
switch time.Tuesday {
case today + 0:
fmt.Println("今天")
case today + 1:
fmt.Println("明天")
default:
fmt.Println("其他")
}
// 2. switch 无表达式(类似 if-else 链)
switch {
case t.Hour() < 12:
fmt.Println("上午好")
case t.Hour() < 17:
fmt.Println("下午好")
default:
fmt.Println("晚上好")
}
// 3. 检测操作系统
switch os := runtime.GOARCH; os {
case "darwin":
fmt.Println("macOS")
case "linux":
fmt.Println("Linux")
default:
fmt.Println(os)
}提示
Go 的 switch 中,每个 case 默认自动 break,无需手动写 break。需要继续执行下一 case 时,显式使用 fallthrough。
fallthrough
fallthrough 使当前 case 执行完后继续执行下一个 case(不判断下一个 case 的条件):
score := 85
switch {
case score >= 90:
fmt.Println("优秀")
fallthrough
case score >= 80:
fmt.Println("良好")
fallthrough
case score >= 70:
fmt.Println("中等")
default:
fmt.Println("继续努力")
}
// 输出:
// 良好
// 中等注意
fallthrough必须是 case 块中的最后一条语句fallthrough会无条件执行下一个 case,不会判断下一个 case 的条件表达式- 一个 case 中不能有多个
fallthrough
类型开关 (Type Switch)
类型开关通过 v := x.(type) 判断接口变量的动态类型:
func checkType(x interface{}) {
switch v := x.(type) {
case int:
fmt.Printf("整数: %d\n", v)
case string:
fmt.Printf("字符串: %s\n", v)
case float64:
fmt.Printf("浮点数: %f\n", v)
case bool:
fmt.Printf("布尔值: %v\n", v)
default:
fmt.Printf("未知类型: %T\n", v)
}
}
func main() {
checkType(42) // 整数: 42
checkType("hello") // 字符串: hello
checkType(3.14) // 浮点数: 3.140000
checkType(true) // 布尔值: true
}注
类型开关中的 v 在每个 case 分支内已被自动转换为对应类型,可直接使用该类型的方法和字段。
随机数
import "math/rand"
// 随机 0~9
num := rand.Intn(10)
// 随机 1~10
num = rand.Intn(10) + 1