go语法速通
变量声明
var
在Go语言中,变量可以使用var
关键字来声明,例如:
var name string = "John"
这里,我们声明了一个类型为字符串的变量name
,并将其初始化为"John"。也可以通过简化声明来利用Go语言的类型推断:
name := "John"
简短声明
这里,我们没有指定name
的类型,而是让编译器根据右侧的值推断出它的类型。这被称为简短声明
。
const
同样,还可以使用const
关键字来声明常量。例如:
const pi = 3.14
这里,我们声明了一个名为pi
的常量,并将其初始化为3.14。由于常量的值不能更改,因此它们在声明时必须初始化。
分支循环
ifelse
在Go语言中,条件语句if
与C或C++非常相似。例如:
if x > 10 {
fmt.Println("x is greater than 10")
} else {
fmt.Println("x is less than or equal to 10")
}
这里,我们检查变量x
是否大于10。如果是,我们将打印一条消息。否则,我们将打印另一条消息。
注意,必须使用大括号来包装if和else语句块,即使它们只有一条语句。
switch
在Go语言中,switch分支结构非常灵活,可以用于取代多个if-else语句,实现更为清晰的代码逻辑。它的语法格式如下:
switch 变量名 {
case 值1:
//执行语句1
case 值2:
//执行语句2
…
default:
//执行默认语句
}
在switch语句中,可以使用任何类型的变量作为判断条件,而不仅仅是整数或字符类型。每个case后面跟随一个需要匹配的值或表达式,当switch中的变量与某个case后面的值或表达式相等时,会执行该case后的语句。如果没有匹配到任何case,将执行default语句,如果没有default语句,将会自动忽略该switch语句。
和C或C++中的switch语句不同的是,在Go语言中,case后的语句块执行完毕后,不需要显式地使用break语句来跳出switch语句,程序会自动跳出switch语句。这样可以避免C或C++中常见的忘记写break导致程序出错的问题。
除了上述基本用法,Go语言的switch语句还支持一些高级用法,例如可以在switch语句中不带变量名,然后在case后面写上条件表达式,实现更为灵活的条件分支。例如:
switch {
case a < 0:
fmt.Println("a is negative")
case a == 0:
fmt.Println("a is zero")
case a > 0:
fmt.Println("a is positive")
}
此外,Go语言的switch语句还支持多条件匹配,例如:
switch a {
case 0, 1, 2:
fmt.Println("a is in the range [0, 2]")
case 3, 4, 5:
fmt.Println("a is in the range [3, 5]")
}
循环
在Go语言中,for循环有几种写法,包括:
- 基本的for循环:
for i := 0; i < 10; i++ {
fmt.Println(i)
}
这里的for循环跟C语言中的循环很类似。for关键字后面紧跟着循环变量的初始化语句,然后是循环条件语句,最后是每次循环后循环变量的更新语句。
- 类似while的for循环:
i := 0
for i < 10 {
fmt.Println(i)
i++
}
这里的for循环跟while循环类似,只有一个条件语句。当条件为真时,循环会一直执行。
- 死循环:
for {
// 死循环
}
这里的for循环后面什么都没有,表示死循环。一般情况下,这种循环需要在循环体内部使用break或return语句来跳出。
除了上述三种for循环写法,Go语言中还提供了range关键字,可以用来遍历数组、切片、映射、字符串等类型的数据结构。
s := []int{1, 2, 3}
for i, v := range s {
fmt.Printf("s[%d] = %d\n", i, v)
}
这里的for循环使用了range关键字,可以同时获取数组或切片中的索引和对应的元素值。如果不需要索引,可以用_(下划线)代替。
在for循环中,还可以使用break语句和continue语句来跳出循环或者继续执行下一次循环。
数据类型
数组
在Go语言中,数组是一种值类型的数据结构,可以在声明时初始化,也可以在后续的代码中进行赋值操作。数组的长度是固定的,而且一旦声明后就不能更改,因此数组的使用范围相对有限。
Go语言中的数组可以是一维或者多维的。一维数组是最简单的数组,它包含若干个相同类型的元素,这些元素按照一定的顺序排列,并且每个元素都可以通过一个整数索引来访问。数组的索引从0开始,到长度减1结束。
在Go语言中,可以通过如下的方式来声明一个数组:
var arr [5]int
这个声明语句创建了一个长度为5的整型数组,每个元素的默认值为0。我们也可以在声明时对数组进行初始化,例如:
var arr = [5]int{1, 2, 3, 4, 5}
这个声明语句创建了一个长度为5的整型数组,每个元素的初始值分别为1、2、3、4、5。
当然,我们也可以只对数组的部分元素进行初始化,未初始化的元素将使用默认值。例如:
var arr = [5]int{1, 2, 3}
这个声明语句创建了一个长度为5的整型数组,前三个元素的值为1、2和3,后两个元素的值为0。
在Go语言中,我们可以使用下标来访问数组的元素,例如:
arr[0] = 10
fmt.Println(arr[0])
这个代码片段将数组的第一个元素赋值为10,并且输出数组的第一个元素的值。
在使用数组时,需要注意数组的长度是固定的,因此不能向一个数组中添加或者删除元素。如果需要使用动态大小的数据结构,可以使用切片(Slice)来代替数组。
切片
当我们使用 Go 语言中的切片(slice)时,通常需要掌握以下几个重要的操作:
- 创建切片:可以使用 make 函数或直接初始化赋值来创建切片。
// 使用 make 函数创建长度为 3 的切片
s1 := make([]int, 3)
// 直接初始化赋值创建切片
s2 := []int{1, 2, 3}
- 访问和修改切片元素:与数组相同,使用索引来访问和修改切片元素。
// 访问切片元素
fmt.Println(s2[0]) // 输出 1
// 修改切片元素
s2[1] = 4
fmt.Println(s2) // 输出 [1 4 3]
- 追加元素:使用 append 函数追加元素到切片中,注意需要将追加后的结果重新赋值给原切片变量。
s2 = append(s2, 5)
fmt.Println(s2) // 输出 [1 4 3 5]
- 切片复制:使用 copy 函数可以将一个切片复制到另一个切片中,但是要确保目标切片的长度足够容纳源切片的元素。
s3 := make([]int, 3)
copy(s3, s2)
fmt.Println(s3) // 输出 [1 4 3]
- 切片长度和容量:切片是一个包含指向底层数组的指针、长度和容量的结构体,可以通过 len 和 cap 函数获取其长度和容量。
fmt.Println(len(s2)) // 输出 4
fmt.Println(cap(s2)) // 输出 4
- 切片切割:使用切片切割操作可以获取切片的一个子切片。
s4 := s2[1:3] // 取出索引为 1 到 2 的元素
fmt.Println(s4) // 输出 [4 3]
map
以下是一个示例代码,展示了如何创建、存储、读取和删除 map 中的键值对:
package main
import "fmt"
func main() {
// 创建一个空 map,键是 string 类型,值是 int 类型
myMap := make(map[string]int)
// 向 map 中添加键值对
myMap["apple"] = 5
myMap["banana"] = 10
myMap["orange"] = 15
// 读取 map 中的键值对
fmt.Println("Number of apples:", myMap["apple"])
fmt.Println("Number of bananas:", myMap["banana"])
fmt.Println("Number of oranges:", myMap["orange"])
// 修改 map 中的键值对
myMap["apple"] = 7
// 删除 map 中的键值对
delete(myMap, "orange")
// 遍历 map 中的键值对
for key, value := range myMap {
fmt.Printf("Key: %s, Value: %d\n", key, value)
}
}
输出结果如下:
Number of apples: 5
Number of bananas: 10
Number of oranges: 15
Key: apple, Value: 7
Key: banana, Value: 10
golang的map是完全无序的,遍历的时候不会按照字母顺序,也不会按照插入顺序输出,而是随机顺序。
range
在Go语言中,range
关键字可以用来遍历数组、切片、映射(map)、字符串等类型的元素。它会返回两个值,第一个值是当前元素的下标或者键,第二个值是当前元素的值。
下面是使用range
遍历一个数组和一个切片的示例代码:
package main
import "fmt"
func main() {
arr := [5]int{1, 2, 3, 4, 5}
for index, value := range arr {
fmt.Printf("arr[%d] = %d\n", index, value)
}
slice := []int{6, 7, 8, 9, 10}
for index, value := range slice {
fmt.Printf("slice[%d] = %d\n", index, value)
}
}
输出结果:
arr[0] = 1
arr[1] = 2
arr[2] = 3
arr[3] = 4
arr[4] = 5
slice[0] = 6
slice[1] = 7
slice[2] = 8
slice[3] = 9
slice[4] = 10
如果我们不需要使用下标,可以使用_
(下划线)来忽略它:
package main
import "fmt"
func main() {
slice := []int{6, 7, 8, 9, 10}
for _, value := range slice {
fmt.Println(value)
}
}
输出结果:
6
7
8
9
10
结构体
结构体声明与赋值
结构体是 Golang 中自定义类型的一种,可以理解为带有字段的数据结构,类似于 C/C++ 中的结构体或者 Python 中的类。
每个字段都有一个名称和类型,可以是内置类型、自定义类型或者其他结构体类型。
下面是一个示例代码:
type User struct {
name string
password string
}
func main() {
user1 := User{"Alice", "123456"}
fmt.Println(user1)
user2 := User{
name: "Bob",
}
fmt.Println(user2)
user3 := &User{
name: "Charlie",
password: "789012",
}
fmt.Println(user3)
user3.password = "654321"
fmt.Println(user3)
}
在上面的代码中,我们定义了一个名为 User
的结构体,包含了 name
和 password
两个字段。我们可以通过结构体类型创建变量,分别用以下几种方式进行初始化:
- 直接按字段顺序赋值
user1 := User{"Alice", "123456"}
- 指定字段名称和值进行初始化
user2 := User{ name: "Bob", }
- 通过指针进行初始化,这里使用了取地址符
&
user3 := &User{ name: "Charlie", password: "789012", }
同时,我们还可以对结构体进行修改,比如对 user3
的 password
字段进行修改。
注意,结构体类型是值类型,因此当我们传递结构体变量作为参数时,会进行值拷贝。如果需要修改原结构体变量,可以使用结构体指针类型。
结构体方法
在 Golang 中,可以为结构体定义方法,方法是一种和结构体绑定的函数,和其他语言的类成员函数有点类似。这样定义的方法可以通过结构体变量来调用。
方法定义的一般形式是:
func (s StructType) methodName(parameters) returnType {
// method body
}
其中 StructType
是结构体类型的名称,后面紧跟着的是方法名。括号中的 s
是接收器,表示方法是绑定在这个结构体类型上的。接收器可以是结构体类型本身或者结构体类型的指针。
如果接收器是结构体类型本身,则调用时会将接收器的一个副本传递给方法;如果接收器是结构体类型的指针,则调用时会将接收器的指针传递给方法。使用指针作为接收器的主要优势是可以避免结构体类型的拷贝,提高代码的性能。
方法的返回值和普通函数的定义方式一样,可以有一个或多个返回值,也可以没有返回值。当方法有返回值时,使用 return
语句返回结果。
下面是一个示例:
type Rectangle struct {
width float64
height float64
}
// 方法定义
func (r Rectangle) area() float64 {
return r.width * r.height
}
// 方法定义(使用指针作为接收器)
func (r *Rectangle) scale(factor float64) {
r.width *= factor
r.height *= factor
}
func main() {
rect := Rectangle{width: 10.0, height: 5.0}
fmt.Println(rect.area()) // 输出:50
rect.scale(2.0)
fmt.Println(rect.width, rect.height) // 输出:20 10
}
在上面的示例中,我们定义了一个 Rectangle
结构体类型,然后为其定义了两个方法:area()
和 scale()
。area()
方法没有使用指针作为接收器,所以调用时会传递一个 Rectangle
值的副本给它。scale()
方法使用指针作为接收器,所以调用时会传递一个 Rectangle
值的指针给它。
在 main()
函数中,我们创建了一个 Rectangle
类型的变量 rect
,调用了 area()
方法并输出了结果。然后调用了 scale()
方法,将 rect
的宽高都乘以了 2,并输出了修改后的结果。
函数与异常处理
函数返回多个值也是 Golang 的一个特色,这种方式可以让代码更加简洁高效。
在 Golang 中,通常将函数的最后一个返回值作为错误信息返回,如果该值为 nil,则表示函数执行成功,否则表示函数执行失败,可以根据这个错误信息进行相应的处理。
下面是一个函数返回多个值的示例:
func divide(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
在这个例子中,函数 divide 接收两个 int 类型的参数 a 和 b,返回一个 int 类型的商和一个 error 类型的错误信息。
在函数体中,首先判断了除数是否为 0,如果是,则返回一个非空的错误信息;否则,返回两个参数的商和一个 nil 错误信息。
转载自:https://juejin.cn/post/7229277460369227831