基本语法与数据类型
Go语言作为一种静态类型、编译型语言,拥有简洁且高效的语法结构。本章将深入介绍Go的基本语法和数据类型,帮助你建立扎实的编程基础。
2.1 第一个 Go 程序
编写第一个Go程序是学习任何编程语言的传统步骤。通过一个简单的"Hello, World!"程序,你将了解Go程序的基本结构和运行方式。
main
函数
每个Go程序的执行都从main
包的main
函数开始。main
函数是程序的入口点,定义了程序启动时执行的代码。
示例:
go
package main
func main() {
// 程序入口
}
package main
: 声明当前文件属于main
包。main
包是可执行程序的入口包。func main()
: 定义main
函数,这是程序的起始点。
fmt.Println()
输出
fmt
包是Go的标准输入输出包,提供了格式化的I/O函数。fmt.Println()
用于在控制台输出文本并换行。
完整的"Hello, World!"程序:
go
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
解释:
import "fmt"
: 导入fmt
包,以便使用其提供的函数。fmt.Println("Hello, World!")
: 输出字符串"Hello, World!"到控制台,并换行。
运行程序:
-
保存文件为
hello.go
。 -
在终端中导航到文件所在目录。
-
执行命令:
go run hello.go
-
输出:
Hello, World!
编译并运行:
-
编译程序:
go build hello.go
这将生成一个可执行文件
hello
(在Windows上为
hello.exe
)。
-
运行可执行文件:
./hello
-
输出:
Hello, World!
2.2 变量与常量
变量和常量是编程语言中存储和管理数据的基本单元。Go语言提供了多种方式来声明和使用变量和常量。
声明变量
在Go中,变量使用var
关键字声明,后跟变量名和类型。
示例:
go
var age int
age = 30
var name string
name = "Alice"
也可以在声明时赋值:
go
var age int = 30
var name string = "Alice"
Go支持类型推断,即编译器可以根据赋值自动推断变量类型:
go
var age = 30 // age为int
var name = "Alice" // name为string
短变量声明
在函数内部,可以使用短变量声明语法:=
来声明并初始化变量,而无需显式指定类型。
示例:
go
age := 30
name := "Alice"
isStudent := true
等同于:
go
var age int = 30
var name string = "Alice"
var isStudent bool = true
常量与枚举
常量使用const
关键字声明,其值在编译时确定,且不可修改。
示例:
go
const Pi = 3.14159
const Greeting = "Hello, World!"
Go不直接支持枚举类型,但可以使用常量组合iota
来模拟枚举。
示例:
go
const (
Sunday = iota
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
)
解释:
iota
是Go的一个预声明标识符,表示常量组中的索引,从0开始。- 上述代码定义了一组代表星期的常量,分别为0到6。
使用枚举模拟:
go
package main
import "fmt"
const (
Red = iota
Green
Blue
)
func main() {
fmt.Println(Red) // 输出: 0
fmt.Println(Green) // 输出: 1
fmt.Println(Blue) // 输出: 2
}
2.3 数据类型
Go语言提供了多种数据类型,分为基本类型和复合类型。掌握这些数据类型是编写有效Go程序的基础。
基本类型
整型
Go支持多种整型,包括有符号和无符号类型,以及不同位数的类型。
- 有符号整型:
int8
,int16
,int32
,int64
,int
- 无符号整型:
uint8
,uint16
,uint32
,uint64
,uint
示例:
go
var a int = 10
var b int64 = 10000000000
var c uint = 20
var d uint8 = 255
浮点型
Go支持float32
和float64
两种浮点类型。
示例:
go
var pi float32 = 3.14
var e float64 = 2.718281828459045
字符串
字符串是由字符组成的序列,使用双引号"
或反引号```包裹。
示例:
go
var greeting string = "Hello, World!"
var multiline string = `This is a
multiline string.`
字符串是不可变的,无法直接修改单个字符。
布尔型
布尔类型表示真或假,使用bool
类型。
示例:
go
var isGoFun bool = true
var isStudent bool = false
复合类型
复合类型是由多个基本类型组成的类型,包括数组、切片、Map、结构体和指针。
数组
数组是具有固定大小和相同类型元素的有序集合。
声明和初始化:
go
var arr [5]int
arr[0] = 10
arr[1] = 20
// 声明并初始化
var arr2 = [3]string{"apple", "banana", "cherry"}
// 使用省略长度
arr3 := [...]float64{1.1, 2.2, 3.3}
遍历数组:
go
for i, v := range arr2 {
fmt.Printf("Index: %d, Value: %s\n", i, v)
}
切片
切片是基于数组的动态数据结构,比数组更灵活和强大。切片的长度和容量可以动态变化。
声明和初始化:
go
// 空切片
var s []int
// 使用make函数
s1 := make([]int, 5) // 长度为5,元素初始化为0
s2 := make([]int, 3, 10) // 长度为3,容量为10
// 字面量初始化
s3 := []string{"Go", "Python", "Java"}
添加元素:
go
s4 := []int{1, 2, 3}
s4 = append(s4, 4, 5)
切片截取:
go
s5 := []int{1, 2, 3, 4, 5}
sub := s5[1:3] // 包含索引1和2,不包含3
fmt.Println(sub) // 输出: [2 3]
切片的底层原理: 切片由指向数组的指针、长度和容量组成。切片的容量表示从切片的起始位置到底层数组末尾的元素数量。
Map
Map是键值对的无序集合,键和值可以是不同的类型。Map在Go中作为内置数据类型提供,类似于Python的字典或Java的HashMap。
声明和初始化:
go
// 声明为空Map
var m map[string]int
// 使用make初始化
m = make(map[string]int)
// 声明并初始化
m1 := map[string]int{
"apple": 5,
"banana": 3,
}
// 使用简短声明
m2 := make(map[string]string)
添加和访问元素:
go
m["orange"] = 7
value := m["apple"]
fmt.Println("apple:", value)
删除元素:
go
delete(m, "banana")
检查键是否存在:
go
value, exists := m["banana"]
if exists {
fmt.Println("banana exists with value:", value)
} else {
fmt.Println("banana does not exist")
}
遍历Map:
go
for key, value := range m {
fmt.Printf("Key: %s, Value: %d\n", key, value)
}
结构体
结构体是由多个字段组成的复合数据类型,可以包含不同类型的数据。
定义和使用结构体:
go
type Person struct {
Name string
Age int
}
func main() {
var p Person
p.Name = "Alice"
p.Age = 30
// 使用字面量初始化
p2 := Person{Name: "Bob", Age: 25}
fmt.Println(p)
fmt.Println(p2)
}
嵌套结构体:
go
type Address struct {
City string
ZipCode string
}
type Employee struct {
Person
Address
Position string
}
func main() {
e := Employee{
Person: Person{Name: "Charlie", Age: 28},
Address: Address{City: "New York", ZipCode: "10001"},
Position: "Developer",
}
fmt.Println(e)
fmt.Println("City:", e.Address.City)
}
方法与结构体:
Go支持为结构体类型定义方法。
go
type Rectangle struct {
Width, Height float64
}
// 方法接收者为指针类型
func (r *Rectangle) Area() float64 {
return r.Width * r.Height
}
func main() {
rect := Rectangle{Width: 10, Height: 5}
fmt.Println("Area:", rect.Area())
}
指针
指针是存储变量内存地址的变量。Go中使用指针可以提高程序的性能,避免大量数据的复制。
声明和使用指针:
go
func main() {
var a int = 10
var p *int = &a // p指向a的地址
fmt.Println("a:", a) // 输出: a: 10
fmt.Println("p:", p) // 输出: p: 地址值
fmt.Println("*p:", *p) // 输出: *p: 10
*p = 20 // 通过指针修改a的值
fmt.Println("a after:", a) // 输出: a after: 20
}
指针与函数:
使用指针作为函数参数,可以在函数中修改传入的变量。
go
func increment(n *int) {
*n += 1
}
func main() {
a := 5
increment(&a)
fmt.Println("a after increment:", a) // 输出: a after increment: 6
}
2.4 类型转换
在Go中,不同类型之间的转换需要显式进行,称为类型转换。Go不支持隐式类型转换,以确保类型安全。
语法:
go
var x int = 10
var y float64 = float64(x)
示例:
go
package main
import "fmt"
func main() {
// 整型与浮点型转换
var a int = 5
var b float64 = float64(a)
var c int = int(b) // 会截断小数部分
fmt.Println(a, b, c) // 输出: 5 5.0 5
// 字符串与字节切片转换
str := "Hello"
bytes := []byte(str)
str2 := string(bytes)
fmt.Println(str, bytes, str2) // 输出: Hello [72 101 108 108 111] Hello
// 类型之间的转换示例:int到string(需要转换为字符)
num := 65
char := string(num)
fmt.Println(char) // 输出: 'A'
}
注意事项:
- 只能在兼容类型之间转换,如整数类型之间、浮点类型之间。
- 字符串与字节切片之间的转换需要使用
[]byte
和string
类型转换。 - 类型转换可能导致数据丢失,如将
float64
转换为int
会截断小数部分。
更多示例:
go
// 将int转换为float64
var i int = 42
var f float64 = float64(i)
fmt.Println(f) // 输出: 42
// 将float64转换为int
var j float64 = 3.14
var k int = int(j)
fmt.Println(k) // 输出: 3
// 将string转换为[]rune
str := "你好"
runes := []rune(str)
fmt.Println(runes) // 输出: [20320 22909]
// 将[]rune转换为string
str2 := string(runes)
fmt.Println(str2) // 输出: 你好
类型转换与算术运算:
在进行算术运算时,操作数必须是相同类型。否则,需要先进行类型转换。
示例:
go
package main
import "fmt"
func main() {
var a int = 10
var b float64 = 3.14
// 错误:不能直接相加不同类型
// c := a + b
// 正确:先转换类型
c := float64(a) + b
fmt.Println(c) // 输出: 13.14
}