Go 基础语法
本章将深入介绍 Go 语言的基础语法,包括变量声明、数据类型、运算符和基本控制结构。理解这些基础概念是掌握 Go 语言的关键。
第一个 Go 程序
让我们从经典的 "Hello, World" 程序开始:
package main
import "fmt"
func main() {
fmt.Println("Hello, Go!")
}
代码结构解析
包声明(Package Declaration)
每个 Go 文件都必须以 package 声明开头。包是 Go 代码组织的基本单位:
package main表示这是一个可执行程序的入口包- 其他包名(如
package utils)用于组织可复用的代码库 - 同一个目录下的所有文件必须属于同一个包
导入语句(Import Statement)
import "fmt" 导入了 Go 标准库中的格式化 I/O 包。fmt 包提供了类似 C 语言 printf/scanf 的格式化功能,但更强大和类型安全。
主函数(Main Function)
func main()是程序的入口点,程序从这里开始执行- 每个可执行程序必须有且只有一个 main 包,其中包含 main 函数
- main 函数不接受参数,也不返回任何值
语句执行
fmt.Println("Hello, Go!") 调用 fmt 包的 Println 函数,输出字符串并自动添加换行符。
变量
变量是程序中存储数据的基本单元。Go 语言提供了多种声明变量的方式,每种方式适用于不同的场景。
变量声明的三种方式
方式一:完整声明(显式类型)
var name string = "张三"
var age int = 25
var isStudent bool = true
这种方式最明确,适合需要强调类型的场景。var 关键字声明变量,后面跟着变量名、类型和初始值。
方式二:类型推断
var name = "张三" // 编译器推断为 string 类型
var age = 25 // 编译器推断为 int 类型
var price = 19.99 // 编译器推断为 float64 类型
Go 编译器会根据右侧的值自动推断变量类型。这种方式代码更简洁,同时保持了类型安全。
方式三:简短声明(仅函数内使用)
name := "张三" // 等价于 var name = "张三"
age := 25 // 等价于 var age = 25
:= 是 Go 的特色语法,只能在函数内部使用。它同时完成声明和初始化,是 Go 中最常用的变量声明方式。
为什么需要多种声明方式?
- 完整声明:当你需要显式指定类型,或声明变量但不立即初始化时使用
- 类型推断:当你希望代码简洁,同时让编译器处理类型时使用
- 简短声明:在函数内部快速声明临时变量时使用,代码更简洁
多个变量声明
使用 var 块声明多个变量
var (
name string = "张三"
age int = 25
city string = "北京"
isActive bool = true
)
这种形式适合在包级别或函数开头集中声明多个变量,提高代码可读性。
简短声明多个变量
name, age, city := "张三", 25, "北京"
可以在一行中同时声明和初始化多个变量,这是 Go 中常见的写法。
变量命名规则
Go 的变量命名遵循以下规则:
- 字符组成:由字母、数字和下划线组成
- 首字符:必须以字母或下划线开头,不能以数字开头
- 大小写敏感:
name和Name是不同的变量 - 关键字限制:不能使用 Go 的关键字(如
var,func,package等)
命名约定:
- 使用驼峰命名法(camelCase):
userName,maxCount - 首字母大写的变量/函数是导出的(public),可被其他包访问
- 首字母小写的变量/函数是未导出的(private),仅在当前包内可见
- 使用有意义的名称:
totalPrice比tp更好
常量
常量是在编译时确定且运行期间不能修改的值。
基本常量声明
const Pi float64 = 3.14159
const MaxConnections = 100
const WelcomeMessage = "欢迎使用本系统"
常量使用 const 关键字声明,可以是任何基本数据类型。
常量组声明
const (
StatusOK = 200
StatusNotFound = 404
StatusError = 500
)
将相关的常量组织在一起,提高代码可读性。
iota 枚举器
iota 是 Go 提供的常量计数器,从 0 开始,在 const 块中每行自动递增 1:
const (
Red = iota // 0
Green // 1
Blue // 2
)
const (
KB = 1 << (10 * iota) // 1 << 0 = 1
MB // 1 << 10 = 1024
GB // 1 << 20 = 1048576
TB // 1 << 30 = 1073741824
)
iota 特别适合定义枚举类型和位掩码,让代码更清晰且易于维护。
数据类型
Go 是静态类型语言,每个变量都有明确的类型。理解数据类型对于编写正确的程序至关重要。
基本类型概览
Go 提供了丰富的基本数据类型,分为以下几类:
| 类别 | 类型 | 说明 |
|---|---|---|
| 整数 | int, int8, int16, int32, int64 | 有符号整数 |
| 整数 | uint, uint8, uint16, uint32, uint64 | 无符号整数 |
| 浮点 | float32, float64 | 浮点数 |
| 复数 | complex64, complex128 | 复数 |
| 布尔 | bool | 布尔值 |
| 字符串 | string | 字符串 |
整数类型详解
有符号整数
var a int8 // -128 ~ 127,占用 1 字节
var b int16 // -32768 ~ 32767,占用 2 字节
var c int32 // -2147483648 ~ 2147483647,占用 4 字节
var d int64 // 极大范围,占用 8 字节
var e int // 平台相关:32位系统为 int32,64位系统为 int64
无符号整数
var f uint8 // 0 ~ 255,常用于表示字节
var g uint16 // 0 ~ 65535
var h uint32 // 0 ~ 4294967295
var i uint64 // 0 ~ 18446744073709551615
var j uint // 平台相关的无符号整数
类型别名
var k byte // uint8 的别名,用于表示原始字节数据
var l rune // int32 的别名,用于表示 Unicode 码点
如何选择整数类型?
- 一般情况下使用
int,它足够处理大多数场景 - 需要明确大小时使用定长类型(如 int32, int64)
- 处理二进制数据或网络协议时使用 byte
- 处理 Unicode 字符时使用 rune
- 需要表示非负数时考虑使用 uint 类型
浮点数类型
var a float32 // 32位浮点数,约 6-7 位有效数字
var b float64 // 64位浮点数,约 15-16 位有效数字(推荐)
// 声明并赋值
price := 19.99 // 默认为 float64
pi := 3.14159265359 // 默认为 float64
浮点数注意事项:
- 默认的浮点数字面量是 float64
- 金融计算需要精确小数时,应使用
math/big包中的 Decimal 类型 - 浮点数比较应该使用容差值,而不是直接比较
// 浮点数比较的正确方式
import "math"
func floatEquals(a, b float64) bool {
return math.Abs(a-b) < 1e-9
}
复数类型
Go 原生支持复数运算,这在科学计算和信号处理中很有用:
var c1 complex64 // 由两个 float32 组成
var c2 complex128 // 由两个 float64 组成(常用)
// 创建复数
c := 3 + 4i
fmt.Println(real(c)) // 3 - 实部
fmt.Println(imag(c)) // 4 - 虚部
// 使用 complex 函数创建
z := complex(3.0, 4.0) // 3 + 4i
布尔类型
var isActive bool = true
var isDeleted bool = false
// 简短声明
isValid := true
布尔运算:
a, b := true, false
fmt.Println(a && b) // false - 逻辑与
fmt.Println(a || b) // true - 逻辑或
fmt.Println(!a) // false - 逻辑非
Go 是强类型语言,布尔值不能与其他类型隐式转换:
// 错误:不能将整数转换为布尔值
// if 1 { ... } // 编译错误
// 正确写法
if x != 0 { ... }
字符串类型
字符串是 Go 中重要的内置类型,用于表示文本数据。
字符串声明
// 双引号字符串(可包含转义字符)
str1 := "Hello, Go!\nWelcome!"
// 反引号原始字符串(保留原样,不处理转义)
str2 := `第一行
第二行
第三行`
// 空字符串
var str3 string = ""
str4 := ""
字符串的特点:
- Go 字符串是不可变的字节序列
- 字符串使用 UTF-8 编码
- 字符串可以包含任意字节,但通常用于存储文本
字符串操作示例:
str := "Hello, Go!"
// 获取长度(字节数,不是字符数)
fmt.Println(len(str)) // 10
// 访问字节
fmt.Println(str[0]) // 72 (H 的 ASCII 码)
fmt.Println(string(str[0])) // H
// 字符串切片
fmt.Println(str[0:5]) // Hello
fmt.Println(str[7:]) // Go!
fmt.Println(str[:5]) // Hello
注意:字符串是不可变的,不能通过索引修改:
str := "Hello"
// str[0] = 'h' // 编译错误:不能修改字符串
指针
指针是 Go 语言中一个重要且独特的概念。理解指针对于编写高效的 Go 程序至关重要,特别是当涉及到函数参数传递、修改数据或避免复制大型数据结构时。
什么是指针?
指针是一个变量,其值是另一个变量的内存地址。打个比方,如果变量是一个存放数据的盒子,那么指针就是一张写着盒子位置的纸条。通过这张纸条,你可以找到盒子并操作里面的数据。
在 Go 中,指针有两个核心操作:
&(取地址运算符):获取变量的内存地址*(解引用运算符):通过地址访问存储在该地址的值
指针的声明与初始化
基本语法:
var ptr *int // 声明一个指向 int 类型的指针
此时 ptr 的值为 nil,因为它还没有指向任何变量。
获取变量地址:
x := 42
ptr := &x // ptr 现在存储了 x 的内存地址
fmt.Printf("x 的值: %d\n", x) // 42
fmt.Printf("x 的地址: %p\n", &x) // 0xc0000b2008(示例地址)
fmt.Printf("ptr 的值: %p\n", ptr) // 0xc0000b2008(与 &x 相同)
fmt.Printf("ptr 指向的值: %d\n", *ptr) // 42
通过指针修改值:
x := 42
ptr := &x
*ptr = 100 // 通过指针修改 x 的值
fmt.Println(x) // 100
指针的零值
指针的零值是 nil,表示指针不指向任何变量:
var ptr *int
fmt.Println(ptr) // <nil>
fmt.Println(ptr == nil) // true
// 解引用 nil 指针会导致 panic
// fmt.Println(*ptr) // panic: runtime error
安全使用指针:
func safeDereference(ptr *int) int {
if ptr == nil {
return 0 // 或返回默认值
}
return *ptr
}
指针与函数
理解指针在函数调用中的作用是 Go 编程的关键。
值传递(默认行为):
Go 函数参数默认是值传递,函数内部修改参数不会影响原变量:
func modifyValue(x int) {
x = 100 // 修改的是副本
}
func main() {
num := 42
modifyValue(num)
fmt.Println(num) // 42(原值不变)
}
指针传递:
传递指针允许函数修改原始变量:
func modifyPointer(x *int) {
*x = 100 // 通过指针修改原变量
}
func main() {
num := 42
modifyPointer(&num)
fmt.Println(num) // 100(原值被修改)
}
何时使用指针参数:
| 场景 | 推荐 | 原因 |
|---|---|---|
| 需要修改原变量 | 指针 | 值传递无法修改原变量 |
| 大型结构体 | 指针 | 避免复制带来的性能开销 |
| 可选参数 | 指针 | nil 可以表示"未提供" |
| 简单值类型 | 值 | 复制开销小,代码更简洁 |
new 函数
Go 提供了 new 函数来创建指针:
ptr := new(int) // 分配内存,返回 *int
fmt.Println(*ptr) // 0(int 的零值)
*ptr = 42
fmt.Println(*ptr) // 42
new(T) 与 &T{} 的区别:
// 对于结构体
type Person struct {
Name string
}
p1 := new(Person) // 返回 *Person,字段为零值
p2 := &Person{} // 同上
p3 := &Person{Name: "张三"} // 可以同时初始化字段
// 对于基本类型
i1 := new(int) // 返回 *int,值为 0
// i2 := &int(42) // 编译错误:不能对基本类型字面量取地址
x := 42
i2 := &x // 正确:对变量取地址
指针与结构体
指针在结构体操作中非常常见:
type Person struct {
Name string
Age int
}
func (p *Person) SetAge(age int) {
p.Age = age // 自动解引用,等价于 (*p).Age = age
}
func main() {
p := &Person{Name: "张三"}
p.SetAge(25) // 方法调用,Go 自动处理指针
fmt.Printf("%+v\n", p) // &{Name:张三 Age:25}
}
结构体指针的自动解引用:
p := &Person{Name: "张三", Age: 25}
// 以下两种写法等价
fmt.Println(p.Name) // 自动解引用
fmt.Println((*p).Name) // 显式解引用
指针与切片/映射
切片和映射本身就是引用类型,通常不需要使用指针:
// 切片:内部已包含指针
func modifySlice(s []int) {
s[0] = 100 // 可以修改底层数组
}
// 映射:内部已包含指针
func modifyMap(m map[string]int) {
m["key"] = 100 // 可以修改原映射
}
func main() {
s := []int{1, 2, 3}
modifySlice(s)
fmt.Println(s) // [100 2 3]
m := map[string]int{"key": 1}
modifyMap(m)
fmt.Println(m) // map[key:100]
}
注意:虽然切片和映射可以修改元素,但如果需要修改切片本身(如长度变化),仍需传递指针:
func appendElement(s *[]int, val int) {
*s = append(*s, val) // 可能触发扩容,改变切片头
}
func main() {
s := []int{1, 2, 3}
appendElement(&s, 4)
fmt.Println(s) // [1 2 3 4]
}
Go 指针的安全性
Go 的指针设计比 C/C++ 更安全:
1. 不支持指针运算:
arr := [5]int{1, 2, 3, 4, 5}
ptr := &arr[0]
// ptr++ // 编译错误:不支持指针运算
// ptr += 4 // 编译错误
这避免了 C/C++ 中常见的指针越界问题。
2. 垃圾回收:
Go 有垃圾回收机制,即使指针指向的内存在函数返回后,只要还有引用存在,就不会被回收:
func createPointer() *int {
x := 42
return &x // 安全:x 的内存不会在函数返回后立即释放
}
3. 空指针检查:
解引用空指针会 panic,但不会导致未定义行为:
var ptr *int
// *ptr = 10 // panic: nil pointer dereference
指针的最佳实践
1. 不要过度使用指针:
// 不推荐:简单值也用指针
func add(a, b *int) *int {
result := *a + *b
return &result
}
// 推荐:简单值使用值传递
func add(a, b int) int {
return a + b
}
2. 方法接收者的选择:
type Counter struct {
value int
}
// 如果需要修改结构体,使用指针接收者
func (c *Counter) Increment() {
c.value++
}
// 如果只是读取,可以使用值接收者
func (c Counter) Value() int {
return c.value
}
// 最佳实践:保持一致性,如果有一个方法用指针,全部都用指针
3. 避免返回局部变量的指针:
虽然 Go 允许这样做(逃逸分析会处理),但可能影响性能:
// 可以工作,但可能触发逃逸分析
func getValue() *int {
x := 42
return &x // x 会逃逸到堆上
}
指针与值类型的区别
| 特性 | 值类型 | 指针类型 |
|---|---|---|
| 赋值 | 复制完整数据 | 复制地址(8字节) |
| 函数传递 | 复制数据 | 复制地址 |
| 修改影响 | 不影响原值 | 影响原值 |
| 比较运算 | 比较数据内容 | 比较地址 |
| 零值 | 类型的零值 | nil |
类型转换
Go 是强类型语言,不同类型之间必须进行显式转换:
// 数值类型转换
var a int = 10
var b float64 = float64(a) // int 转 float64
var c float64 = 3.14
var d int = int(c) // float64 转 int(截断小数部分)
// 字符串和字节切片转换
str := "Hello"
bytes := []byte(str) // 字符串转字节切片
str2 := string(bytes) // 字节切片转字符串
// 字符串和 rune 切片转换
runes := []rune(str) // 字符串转 rune 切片(处理 Unicode)
str3 := string(runes) // rune 切片转字符串
类型转换注意事项:
- 高精度转低精度可能丢失数据
- 浮点数转整数会截断小数部分(不是四舍五入)
- 字符串和数字之间不能直接转换,需要使用 strconv 包
import "strconv"
// 字符串转整数
num, err := strconv.Atoi("123") // num = 123
// 整数转字符串
str := strconv.Itoa(123) // str = "123"
// 字符串转浮点数
f, err := strconv.ParseFloat("3.14", 64)
// 浮点数转字符串
s := strconv.FormatFloat(3.14, 'f', 2, 64) // s = "3.14"
零值
在 Go 中,声明但未初始化的变量会被赋予该类型的"零值":
var i int // 0
var f float64 // 0
var b bool // false
var s string // "" (空字符串)
var arr [5]int // [0 0 0 0 0]
var p *int // nil
var m map[string]int // nil
var slice []int // nil
零值机制确保变量总是有确定的值,避免了未初始化变量带来的不确定性。
运算符
Go 提供了丰富的运算符,用于执行各种计算和操作。
算术运算符
a, b := 10, 3
fmt.Println(a + b) // 13 - 加法
fmt.Println(a - b) // 7 - 减法
fmt.Println(a * b) // 30 - 乘法
fmt.Println(a / b) // 3 - 整除(整数相除结果也是整数)
fmt.Println(a % b) // 1 - 取余(求模)
整数除法的特点:
fmt.Println(7 / 2) // 3(不是 3.5)
fmt.Println(7.0 / 2) // 3.5(浮点数除法)
比较运算符
a, b := 10, 3
fmt.Println(a == b) // false - 等于
fmt.Println(a != b) // true - 不等于
fmt.Println(a > b) // true - 大于
fmt.Println(a < b) // false - 小于
fmt.Println(a >= b) // true - 大于等于
fmt.Println(a <= b) // false - 小于等于
Go 支持链式比较:
x := 5
fmt.Println(1 < x && x < 10) // true
逻辑运算符
a, b := true, false
fmt.Println(a && b) // false - 逻辑与(两边都为真才为真)
fmt.Println(a || b) // true - 逻辑或(一边为真即为真)
fmt.Println(!a) // false - 逻辑非(取反)
短路求值:
// && 短路:左边为假时,右边不执行
if false && expensiveFunction() {
// expensiveFunction 不会被执行
}
// || 短路:左边为真时,右边不执行
if true || expensiveFunction() {
// expensiveFunction 不会被执行
}
位运算符
位运算符直接操作二进制位,在底层编程和性能优化中很有用:
a, b := 5, 2 // 二进制: 5 = 101, 2 = 010
fmt.Println(a & b) // 0 - 按位与(都为1才为1)
fmt.Println(a | b) // 7 - 按位或(有一个为1即为1)
fmt.Println(a ^ b) // 7 - 按位异或(不同为1)
fmt.Println(a &^ b) // 5 - 位清除(AND NOT)
fmt.Println(a << b) // 20 - 左移(相当于乘以 2^b)
fmt.Println(a >> b) // 1 - 右移(相当于除以 2^b)
位运算应用示例:
// 使用位运算设置权限标志
const (
ReadPermission = 1 << 0 // 0001 = 1
WritePermission = 1 << 1 // 0010 = 2
ExecPermission = 1 << 2 // 0100 = 4
)
// 组合权限
permissions := ReadPermission | WritePermission // 0011 = 3
// 检查权限
hasRead := permissions&ReadPermission != 0 // true
hasExec := permissions&ExecPermission != 0 // false
赋值运算符
a := 10
a += 5 // a = a + 5 = 15
a -= 3 // a = a - 3 = 12
a *= 2 // a = a * 2 = 24
a /= 4 // a = a / 4 = 6
a %= 5 // a = a % 5 = 1
// 位运算赋值
a &= 3
a |= 3
a ^= 3
a <<= 2
a >>= 2
字符串操作深入
strings 包常用函数
import "strings"
str := "Hello, Go!"
// 大小写转换
fmt.Println(strings.ToUpper(str)) // HELLO, GO!
fmt.Println(strings.ToLower(str)) // hello, go!
// 包含判断
fmt.Println(strings.Contains(str, "Go")) // true
fmt.Println(strings.HasPrefix(str, "Hello")) // true
fmt.Println(strings.HasSuffix(str, "Go!")) // true
// 查找和替换
fmt.Println(strings.Index(str, "Go")) // 7
fmt.Println(strings.Replace(str, "Go", "World", 1)) // Hello, World!
// 分割和拼接
parts := strings.Split("a,b,c", ",") // ["a", "b", "c"]
fmt.Println(strings.Join(parts, "-")) // a-b-c
高效字符串构建
字符串在 Go 中是不可变的,频繁拼接字符串会产生大量临时对象。对于大量字符串拼接,应使用 strings.Builder:
import "strings"
// 方式一:+ 拼接(适合少量字符串)
str := "Hello" + ", " + "Go!"
// 方式二:fmt.Sprintf(适合格式化)
str := fmt.Sprintf("Hello, %s!", "Go")
// 方式三:strings.Builder(适合大量拼接,性能最好)
var builder strings.Builder
builder.WriteString("Hello")
builder.WriteString(", ")
builder.WriteString("Go!")
result := builder.String() // Hello, Go!
// 方式四:bytes.Buffer(类似 Builder,但功能更多)
import "bytes"
var buffer bytes.Buffer
buffer.WriteString("Hello")
buffer.WriteString(", ")
buffer.WriteString("Go!")
result := buffer.String()
性能对比:
+拼接:每次都会产生新字符串,时间复杂度 O(n²)strings.Builder:内部使用可变缓冲区,时间复杂度 O(n)- 大量拼接时,Builder 比 + 快数十倍甚至上百倍
输入输出
fmt 包格式化输出
import "fmt"
// 基本输出
fmt.Print("Hello") // 不换行
fmt.Println("Hello") // 换行
fmt.Printf("Hello %s\n", "Go") // 格式化输出
// 格式化占位符
name := "张三"
age := 25
fmt.Printf("姓名: %s, 年龄: %d\n", name, age)
// 输出到字符串
str := fmt.Sprint("Hello") // 返回字符串
str = fmt.Sprintf("Hello %s", "Go") // 格式化返回字符串
str = fmt.Sprintln("Hello") // 返回带换行的字符串
常用格式化动词:
| 动词 | 说明 | 示例 |
|---|---|---|
| %v | 默认格式 | fmt.Printf("%v", 123) // 123 |
| %T | 类型 | fmt.Printf("%T", 123) // int |
| %d | 十进制整数 | fmt.Printf("%d", 123) // 123 |
| %b | 二进制 | fmt.Printf("%b", 5) // 101 |
| %o | 八进制 | fmt.Printf("%o", 8) // 10 |
| %x | 十六进制(小写) | fmt.Printf("%x", 255) // ff |
| %X | 十六进制(大写) | fmt.Printf("%X", 255) // FF |
| %f | 浮点数 | fmt.Printf("%f", 3.14) // 3.140000 |
| %e | 科学计数法 | fmt.Printf("%e", 1234.5) // 1.234500e+03 |
| %s | 字符串 | fmt.Printf("%s", "hello") // hello |
| %q | 带引号的字符串 | fmt.Printf("%q", "hello") // "hello" |
| %p | 指针 | fmt.Printf("%p", &x) // 0xc0000... |
| %t | 布尔值 | fmt.Printf("%t", true) // true |
格式化修饰符:
num := 42
fmt.Printf("|%d|\n", num) // |42|
fmt.Printf("|%5d|\n", num) // | 42| (宽度5,右对齐)
fmt.Printf("|%-5d|\n", num) // |42 | (宽度5,左对齐)
fmt.Printf("|%05d|\n", num) // |00042| (宽度5,前导0)
pi := 3.14159
fmt.Printf("|%.2f|\n", pi) // |3.14| (保留2位小数)
fmt.Printf("|%8.2f|\n", pi) // | 3.14| (宽度8,2位小数)
读取输入
import (
"bufio"
"fmt"
"os"
)
// 读取单个值
var name string
fmt.Print("请输入姓名: ")
fmt.Scan(&name)
fmt.Println("你好,", name)
// 读取多个值
var name string
var age int
fmt.Print("请输入姓名和年龄: ")
fmt.Scan(&name, &age)
// 读取整行(推荐)
reader := bufio.NewReader(os.Stdin)
fmt.Print("请输入一行文字: ")
line, _ := reader.ReadString('\n')
fmt.Println("你输入的是:", line)
注释
良好的注释习惯是编写可维护代码的重要部分。
// 单行注释:解释代码的某一行
/*
多行注释
用于较长的说明
或临时禁用代码块
*/
// 文档注释:出现在包、函数、类型声明前,会被 godoc 工具提取
// Add 返回两个整数的和
// 参数 a: 第一个加数
// 参数 b: 第二个加数
// 返回值: 两数之和
func Add(a, b int) int {
return a + b
}
注释规范:
- 使用
//进行单行注释 - 包注释应该放在 package 声明前
- 导出(首字母大写)的函数、类型、变量必须有文档注释
- 注释应该解释"为什么"而不是"是什么"
小结
本章我们学习了 Go 语言的基础语法:
- 程序结构:包声明、导入语句、main 函数
- 变量声明:var 声明、类型推断、简短声明
- 常量:const 声明、iota 枚举器
- 数据类型:整数、浮点数、布尔值、字符串
- 类型转换:显式转换规则
- 运算符:算术、比较、逻辑、位运算
- 字符串操作:strings 包、高效字符串构建
- 输入输出:fmt 包格式化
- 注释:单行、多行、文档注释
练习
- 变量练习:声明不同类型的变量并打印,观察零值
- 类型转换:实现摄氏度到华氏度的转换
- 字符串操作:编写程序统计字符串中某个字符出现的次数
- 位运算:实现一个简单的权限管理系统
- 格式化输出:美化打印一个表格