常量
在Go语言中,常量是使用 const
关键字定义的,并且一旦被赋值后,它们的值在程序运行期间不能改变。常量可以是字符、字符串、布尔或数值类型。
基本特性
- 不可修改:一旦一个常量被定义,它的值就不能被更新。
- 编译时求值:常量表达式的值在编译时就已经确定,而不是在运行时。
- 提高性能:使用常量可以避免程序运行时进行重复计算,从而提高效率。
- 类型安全:你可以为常量指定具体类型,以确保数据安全和清晰的意图表达。
定义常量
你可以单独声明每个常量,也可以在一个块中声明一组相关的常量:
go
package main
import "fmt"
const pi = 3.14159265358979
const (
a = 1
b = "2"
)
func main() {
fmt.Println(pi)
fmt.Println(a, b)
//a = 2 // 报错,不能修改常量
}
类型和无类型
Go语言中的常量可以是有类型或无类型。无类型的常数拥有更高的灵活性,因为它们可以根据需要自动适应其环境(赋给不同类型变量时自动转换)。
go
const c = 123 // 无类型整数
var d int64 = c // 可以直接赋给int64变量
fmt.Println(d)
const e float32 = 3.14 // 明确指定float32类型
//var f int32 = e // 这会引发编译错误因为c已经明确其为float32型
iota枚举器
Go语言提供了一个特殊的关键字 iota
,这个关键字用来生成一系列相似规则初始化的整数。每当 const 出现时将被重置为0, 每定义一个新项 iota
就会自增,这种机制非常适合用于定义枚举类数据。
go
package main
import "fmt"
// 常量未被使用,编译不会报错
const (
North = iota // 0
East // 1
South // 2
West // 3
)
// 整数自增,就算是从负数开始
const (
v1 = iota - 3 // 从0 开始,如果需要可以自定义偏移量
_ // 占位符,空出一个数
v2 // -1
v3 // 0
v4 // 1
)
func main() {
fmt.Println(v1, v2, v3, v4)
}
常量未使用不报错
1. 变量未使用报错
当你声明一个局部变量但没有在其作用域内使用它时,Go编译器会报错。这是一种编程实践上的鼓励,目的是帮助开发者避免程序中出现无用代码或潜在的错误。例如,声明了一个变量却没有使用可能意味着某些逻辑被遗漏或者写错了。
go
func myFunction() {
var a int = 5 // 如果后续代码中没有使用到a, 编译将失败
}
2. 常量未使用不报错
对于常量,情况则不同。你可以声明一个或多个常量而不立即使用它们,编译器不会报错。这主要是因为常量通常定义为包级别(全局),供整个包甚至外部包(如果是公开的)中多处地方引用。
由于常量值在编译时就已确定,并且存储方式高效(通常直接嵌入到最终二进制代码中),所以即使定义了但未被直接引用也不会导致运行时性能损失或额外内存消耗。
go
const Pi = 3.14 // 即使Pi没有被立即使用, 这也是合法且无错误提示的。
3. 设计哲学
Go语言设计者通过这种方式强化了一些语言设计原则:
- 对于局部变区域性管理:鼓励只保留必要的局部变量来减少复杂度和潜在错误。
- 对于全局区域放宽限制:允许灵活定义可重复利用、跨文件甚至跨包访问的静态数据。
条件语句
if语句
在Go语言中,if
语句是用来测试某个条件的布尔表达式的真假,并根据结果执行相应代码块的控制结构。Go语言中的 if
语句具有清晰简洁的语法,和其他编程语言类似,但也有一些独特之处。
基本结构
基本的 if
语句包括一个条件表达式和一个当条件为真时执行的代码块:
go
if 条件 {
// 条件为 true 时执行
}
条件 必须是一个返回布尔值(true 或 false)的表达式或者变量。
完整结构
go
package main
import "fmt"
func main() {
var condition1 = false
var condition2 = true
if condition1 {
fmt.Println("条件1为真")
// 对于多重条件判断,可以使用一个或多个 `else if` 分支:
} else if condition2 {
fmt.Println("条件2为真")
} else {
// 如果你还需要在条件不满足(即为 false)时执行某些操作,则可以使用 `else` 子句:
fmt.Println("条件1和条件2都为假")
}
}
初始化语句
Go 的 if
可以包含一个可选的初始化语句,在评估条件之前运行。这常用于局部变量定义和初始化。该变量作用域限定在 if
, else if
, 和 else
块内。
go
if 初始化声明的变量; 条件 {
// 使用初始化声明的变量进行操作
}
例如:
go
package main
import (
"fmt"
)
func main() {
// if语句内可以访问x
if x := computeValue(); x > 0 {
fmt.Printf("x 的值为%d,是正数", x)
} else if x < 0 {
fmt.Printf("x 的值为%d,是负数", x)
} else {
fmt.Printf("x 的值为%d,不是正数也不是负数", x)
}
}
func computeValue() int {
var val int
fmt.Println("输入一个数字:")
fmt.Scanf("%d", &val)
return val
}
嵌套及多条件的与或
go
if 1 > 0 || 2 < 3 {
fmt.Println("第一个判断为真")
if true == false && true == true {
fmt.Println("第二个判断为真")
} else {
fmt.Println("第二个判断为假")
}
}
注意事项
- 在 Go 中,不能像python那样将非布尔类型(如整数)直接用作布尔表达式。
- 每个左大括号
{}
必须放在控制结构关键字 (if
,else
) 同一行上。 - 初始化子句中声明的任何变量都只能在所有相关联的逻辑块内访问。
练习
用户输入一个数字,看是否与程序随机生成的一样,后续学了循环再做成一直猜的
go
package main
import (
"fmt"
"math/rand"
)
func main() {
fmt.Println("猜猜看,请输入我想1-100数字:")
var targetNumber int = rand.Intn(100) + 1 // 随机生成1-100的数字,左闭右开,不加一的话是0到99
if number := inputNumber(); number > targetNumber {
fmt.Printf("比 %d 小,答案是:%d", number, targetNumber)
} else if number < targetNumber {
fmt.Printf("比 %d 大,答案是:%d", number, targetNumber)
} else {
fmt.Println("恭喜!猜对了!")
}
}
func inputNumber() int {
var number int
_, err := fmt.Scanf("%d", &number)
if err != nil {
fmt.Println("输入有误请重新输入!")
return inputNumber()
}
return number
}
switch语句
switch
语句是一种控制结构,用于基于不同的条件执行不同的代码块。
在Go语言中,switch
语句提供了一种处理多路分支的方式,它比多个 if-else
块更清晰和更直观。Go的 switch
语句有几个特点使其与其他语言中的类似结构不同:
1. 自动终止
在Go中,每个 case
分支默认自带 break
,意味着不需要显式地在每个 case
结尾添加 break
。这避免了其他语言常见的通过忘记写 break
而导致的错误。
2. 不限制常量
Go 的 switch
不仅可以对常量进行判断,还可以对表达式或者函数返回值进行判断。
3. 可以省略条件表达式
如果省略了条件表达式,则默认为真(true),这使得它可以替代多层嵌套的if-else结构。
基本用法
go
x := 2
switch x {
case 1:
fmt.Println("为1")
case 2:
fmt.Println("为2")
default:
fmt.Println("不为1也不为2")
}
使用表达式
go
score := 88
switch {
case score >= 90:
fmt.Println("优")
case score >= 80:
fmt.Println("良")
default:
fmt.Println("及格或差")
}
在上面的例子中,由于没有给出具体要比较的变量或值,所以每一个 case 的条件是一个返回布尔值的表达式。
多值匹配
一个 case 可以同时测试多个可能匹配项:
go
number := rand.Intn(10)
switch number {
case 1, 2, 3:
fmt.Println("1-3")
case 4, 5, 6:
fmt.Println("4-6")
case 7, 8, 9:
fmt.Println("7-9")
default:
fmt.Println("0")
}
类型匹配(Type Switch)
类型匹配让你能够根据类型而非值来决定执行哪段代码:
go
package main
import "fmt"
func main() {
catchType(123)
catchType("adb")
catchType(3.4)
catchType(true)
}
/*
空接口可以接收任何类型的值。关于接口后面我们再展开学习
*/
func catchType(i interface{}) {
// 关键字 type 告诉 Go 这是一个特殊的 switch 声明,即"Type Switch"。
switch v := i.(type) {
case int:
fmt.Printf("%v 是 int 类型\n", v)
case float64:
fmt.Printf("%v 是 float64 类型\n", v)
case string:
fmt.Printf("%v 是 string 类型\n", v)
default:
fmt.Printf("%v 是未知类型!\n", v)
}
}
这里使用特殊形式 .()
来断言类型,并且使用关键字 type
, 这样就能够根据接口变量实际存储内容类型来执行不同代码块。
goto语句
在 Go 语言中,goto
语句是一种无条件跳转语句,它允许程序的执行流程跳转到同一函数体内部的任意位置。goto
语句与标签(label)一起使用,标签是紧跟在冒号后面的一种标识符,用于标记代码中的特定位置。标签名必须是唯一的。
尽管 goto
在其他语言中常用于控制流,但在 Go 中通常不推荐使用,因为它可能导致代码难以理解和维护。
基本语法
go
labelName:
// 一些代码
// 跳转到标签位置
goto labelName
// 跳转后的代码不会被执行
示例代码
go
package main
import "fmt"
func main() {
login("123")
login("abc")
}
func login(password string) {
// 不能写在这里,不然会把if语句的代码也归属到fail这个标签,循环执行
//fail:
// fmt.Println("登录失败")
if password == "123" {
goto success
} else {
goto fail
}
fail:
fmt.Println("登录失败")
// 即使登录失败,也会执行success标签下的代码
success:
fmt.Println("登录成功")
}
// 标签不能写在函数外面
//fail:
// fmt.Println("登录失败")
输出:
登录成功
登录失败
登录成功