变量和常量
变量
在 Go 语言中变量是用于存储和操作数据的名称,变量的本质其实就是计算机的一小块内存,用于存储数据,在程序运行的过程中变量的数据值可以被修改和更新,在 Go 语言中,变量必须先声明后使用。
定义变量有以下五种方式:
- 第一种:指定变量的数据类型,然后赋值。
语法格式:
// 指定变量的数据类型,然后赋值。
var 变量名 数据类型 = 值
// 指定变量的数据类型,然后赋值。
var 变量名 数据类型
变量名 = 值
示例:
// 写在一行的格式
var v1 string = "Hello Go!"
// 打印输出
fmt.Println(v1)
// 分开写法
var v1 string
v1 = "Hello Go!"
// 打印输出
fmt.Println(v1)
- 第二种:根据值自行判断变量的数据类型(类型推断Type inference)
语法格式:
// 类型推断
var 变量名 = 值
示例:
// 类型推断
var v1 = "Hello" // string类型
var v2 = 100 // int类型
var v3 = 8.888 // float64类型
var v4 = 'A' // rune类型(实际是int32的别名)
var v5 = true // bool类型
// 打印输出
fmt.Printf("v1数据类型%T,值是:%s\n", v1, v1)
fmt.Printf("v2数据类型%T,值是:%d\n", v2, v2)
fmt.Printf("v3数据类型%T,值是:%0.3f\n", v3, v3)
fmt.Printf("v4数据类型%T,值是:%v\n", v4, v4)
fmt.Printf("v5数据类型%T,值是:%v\n", v5, v5)
- 第三种:省略var使用
:=
自动推断变量类型(只能在函数内部使用,多个变量同时定义时,至少保证一个是新变量),否则会导致编译错误(简短定义)。
语法格式:
// 简短定义
变量名 := 值
示例:
// 简短定义
name := "Tom" // string类型
age := 20 // int类型
// 打印输出
fmt.Println(name, age)
- 第四种:同时定义多个变量
方式一:以逗号分隔,声明与赋值分开,若不赋值,存在默认值。(用于定义同一种数据类型的变量)
语法格式:
// 以逗号分隔,声明与赋值分开,若不赋值,存在默认值。(用于定义同一种数据类型的变量)
// 注意事项:变量有几个,值就要有几个
var 变量名1, 变量名2, 变量名3, ..., 变量名n 数据类型 = 值1, 值2, 值3,..., 值n
// 以逗号分隔,声明与赋值分开,若不赋值,存在默认值。(用于定义同一种数据类型的变量)
// 注意事项:变量名有几个,值就要有几个
var 变量名1, 变量名2, 变量名3,..., 变量名n 数据类型
变量名1, 变量名2, 变量名3, ..., 变量名n = 值1, 值2, 值3,..., 值n
示例:
// 以逗号分隔,声明与赋值分开,若不赋值,存在默认值。(用于定义同一种数据类型的变量)
// 注意事项:变量名有几个,值就要有几个
var a, b, c string = "Golang", "Java", "C++"
fmt.Println(a, b, c)
// 以逗号分隔,声明与赋值分开,若不赋值,存在默认值。(用于定义同一种数据类型的变量)
// 注意事项:变量名有几个,值就要有几个
var a, b, c int
a, b, c = 10, 20, 30
fmt.Println(a, b, c)
方式二:同时定义多个变量使用类型推断的方式。
语法格式:
// 可以是相同数据类型,也可以是不同的数据类型
// 这种方式必须赋值,不能分开写
// 注意事项:变量名有几个,值就要有几个
var 变量名1, 变量名2, 变量名3, ..., 变量名n = 值1, 值2, 值3,..., 值n
示例:
// 多个变量声明,相同数据类型
// 注意事项:变量名有几个,值就要有几个
var a, b, c = 11, 22, 33
fmt.Println(a, b, c)
// 多个变量声明,不同数据类型
// 注意事项:变量名有几个,值就要有几个
var a, b, c, d = 11, 8.88, "Hello Go!", true
fmt.Println(a, b, c, d)
方式三:同时定义多个变量,省略var使用 :=
自动推断变量类型(简短定义)
语法格式:
// 使用简单定义的方式,声明多个变量
// 注意事项:变量名有几个,值就要有几个
变量名1, 变量名2, 变量名3, ..., 变量名n := 值1, 值2, 值3,..., 值n
示例:
// 使用简单定义的方式,声明多个变量
// 注意事项:变量名有几个,值就要有几个
a, b, c := 33, 9.99, 'A'
fmt.Println(a, b, c)
// 使用简单定义的方式,声明多个变量
// 注意事项:左边必须要有一个变量是新的,且右边的值需要符合左边定义的数据类型,否则编译报错。
// a变量int类型、b变量float64类型、c变量rune类型【int32类型】
a, b, c := 33, 9.99, 'A'
fmt.Println(a, b, c)
// 错误
// a, b, c := 55, 8.88, 'B' // no new variables on left side of :=
// fmt.Println(a, b, c)
// 错误
// a, b, c 变量则是需要和上面定义的数据类型一致才可以,否则编译不通过
// a, b, c, d := 55.6, 8.88, 'B', false // cannot use 55.6 (untyped float constant) as int value in assignment (truncated)
// 上面变量a=33,默认是int类型,这里赋值55.6是float64类型,编译错误。
// fmt.Println(a, b, c, d)
// 正确
// 这里的d变量是新的变量。
a, b, c, d := 55, 8.88, 'B', false
fmt.Println(a, b, c, d)
- 第五种:变量分组
语法格式:
// 变量分组定义的语法格式
// 声明数据类型的方式
var (
变量名1 变量类型1 = 值1
变量名2 变量类型2 = 值2
变量名3 变量类型3 = 值3
...
变量名n 变量类型n = 值n
)
// 变量分组定义的语法格式
// 使用类型推断的方式
var (
变量名1 = 值1
变量名2 = 值2
变量名3 = 值3
...
变量名n = 值n
)
示例:
// 正确示例
var (
n1 int = 111 // int类型
n2 float64 = 7.777 // float64类型
n3 string = "hello" // string类型
)
fmt.Println(n1, n2, n3)
// 正确示例,省略数据类型,根据类型推断
var (
n1 = 111 // int类型
n2 = 7.777 // float64类型
n3 = "hello" // string类型
)
fmt.Println(n1, n2, n3)
// 错误示例
var (
n1 = 111 // int类型
n2 // syntax error: unexpected newline, expected type
n3 = "hello" // string类型
)
fmt.Println(n1, n2, n3)
// 正确示例
var (
n1 = 111 // int类型
n2 float64 // 没有赋值,需要声明是什么数据类型
n3 = "hello" // string类型
)
fmt.Println(n1, n2, n3)
变量使用时的注意事项
- 变量必须先声明(定义)才能使用。
- Go语言是静态语言,要求变量的类型和赋值的类型必须一致。
- 变量名不能冲突。(同一个作用于域内不能冲突)。
- 简短定义方式,左边的变量名至少有一个是新的。
- 简短定义方式,不能定义全局变量。
- 变量的零值。也叫默认值。
- 变量定义了就要使用,否则无法通过编译。
- 变量必须先声明(定义)才能使用。
package main
import "fmt"
func main() {
// 变量必须先声明(定义)才能使用。
var age int
fmt.Println("Age:", age) // 正确,age已经声明
// fmt.Println("Name:", name) // 错误,name未声明
// name := "John" // 错误,变量使用在声明之前
var score int
score = 90
fmt.Println("Score:", score) // 正确,score已经声明并赋值
}
- Go语言是静态语言,要求变量的类型和赋值的类型必须一致。
package main
import "fmt"
func main() {
// Go语言是静态语言,要求变量的类型和赋值的类型必须一致。
var age int // 声明一个整数类型的变量age
age = 26 // 赋值整数值给age
var name string // 声明一个字符串类型的变量name
name = "John" // 赋值字符串值给name
var ratio float64 // 声明一个浮点数类型的变量ratio
ratio = 3.14 // 赋值浮点数值给ratio
// var score int = 90 也可以在声明时赋值
fmt.Println("Age:", age)
fmt.Println("Name:", name)
fmt.Println("Ratio:", ratio)
}
- 变量名不能冲突。(同一个作用于域内不能冲突)
package main
import "fmt"
func main() {
// 变量名不能冲突。(同一个作用于域内不能冲突)
var age int = 26
fmt.Println("Age:", age)
// var age int = 30 错误,重复声明同名变量
var name string = "John"
fmt.Println("Name:", name)
// 简短变量声明 := 运算符用于在声明变量的同时进行赋值操作,它会自动推断变量的类型。
// name := "Doe" // 错误,重复声明同名变量
// ...
}
- 简短定义方式,左边的变量名至少有一个是新的。
package main
import "fmt"
func main() {
// 简短定义方式,左边的变量名至少有一个是新的。
age := 26 // 简短声明,声明age变量并赋值为26
age, name := 30, "John" // 错误,age已经声明过
fmt.Println("Age:", age)
fmt.Println("Name:", name)
}
- 简短定义方式,不能定义全局变量。
package main
import "fmt"
// 这里尝试使用简短变量声明定义全局变量,会导致编译错误
// age := 26 // 简短定义全局变量
var age = 26
func main() {
fmt.Println("Age:", age)
}
- 变量的零值。也叫默认值。
package main
import "fmt"
func main() {
// 变量的零值。也叫默认值。
var v1 int // 默认值:0
var v2 string // 默认值:""
var v3 bool // 默认值:false
var v4 float64 // 默认值:0.0
var v5 complex128 // 默认值:(0+0i)
fmt.Println(v1)
fmt.Println(v2)
fmt.Println(v3)
fmt.Println(v4)
fmt.Println(v5)
}
- 变量定义了就要使用,否则无法通过编译。
package main
func main() {
var x int
// 定义了变量 x,但未使用它
var y string = "Hello, world!"
// 定义了变量 y,并使用了它
_ = y // 使用了变量 y,但是将其赋值给了匿名变量 _
// 此处未使用变量 x,将会导致编译错误
}
全局变量
在 Go 语言中,全局变量是在函数外部声明的变量,它们具有在整个程序包范围内可见的特性。全局变量的作用是存储在程序执行期间需要在多个函数之间共享的数据。
以下是全局变量的一些常见作用:
- 数据共享:全局变量可以在整个程序包中的多个函数中访问和修改,使得数据在不同函数之间共享变得更加方便。这对于需要在不同函数之间传递数据或共享状态的情况非常有用。
- 状态维护:全局变量可以用于维护程序的状态。状态指的是程序在执行过程中的特定条件或属性。通过在全局范围内定义变量,可以在程序的不同部分中访问和更新这些状态,以便程序在不同的执行点上保持一致的行为。
- 配置信息:全局变量可以用来保存程序的配置信息,例如数据库连接字符串、日志级别、服务器地址等。通过将这些配置信息存储在全局变量中,可以在整个程序中轻松地访问这些信息,而不必在每个需要使用它们的函数中传递它们。
需要注意的是,尽管全局变量具有上述的方便之处,但过度使用全局变量可能会导致代码的可读性和可维护性下降。因此,在使用全局变量时应该谨慎,并仔细考虑是否有更好的方式来组织和传递数据,例如使用函数参数和返回值来实现数据的传递和共享。
语法格式:
var 变量名称 数据类型
示例:
package main
import "fmt"
// 全局变量
var counter int // 默认值:0
func main() {
counter++
counter++
fmt.Println(counter) // 输出结果:2
}
常量
相对变量来说,常量是一种固定不变的值,常量的本质其实就是计算机的一小块内存,用于存储数据,在程序运行的过程中常量的数值是不可以被修改和更新的。
语法格式:
const 常量名 数据类型 = 值
const 常量名 = 值
示例:
package main
import "fmt"
func main() {
// 常量
// 显式类型定义
// 指定了数据类型
const PI float64 = 3.1415926
fmt.Println(PI)
// 隐式类型定义
// 没有指定数据类型,根据编译器自动推断,这里的30会被推断为int类型
const DAY = 30
fmt.Println(DAY)
}
常量和变量分组一样,也可以同时声明多个常量。
语法格式:
// 同时声明多个常量
// type类型可以省略不写,通过类型推断机制
// 常量组中如不指定类型和初始化值,则与上一行非空常量右值相同
const (
常量名1 [数据类型1] = 值1
常量名2 [数据类型2] = 值2
常量名3 [数据类型3] = 值3
...
常量名n [数据类型n] = 值n
)
示例:
package main
import "fmt"
func main() {
// 同时声明多个常量
const (
SPRING = 0
SUMMER = 1
AUTUMN = 2
WINTER = 3
)
fmt.Println(SPRING, SUMMER, AUTUMN, WINTER)
}
package main
import "fmt"
func main() {
// 常量组中如不指定类型和初始化值,则与上一行非空常量右值相同
const (
a = 100
b // 默认和上一行相同【数据类型和值都是一样的】
c // 默认和上一行相同【数据类型和值都是一样的】
d float64 = 5.555
e string = "hello"
f // 默认和上一行相同【数据类型和值都是一样的】
g rune = 'A'
)
fmt.Println(a, b, c, d, e, f, g)
}
常量使用时的注意事项
- 常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。
- 不曾使用的常量,在编译的时候,是不会报错的。
- 显示指定类型的时候,必须确保常量左右值类型一致,需要时可做显示类型转换。这与变量就不一样了,变量是可以是不同的类型值。
iota
在 Go 语言中 iota
是一个预定义的标识符,用于定义枚举常量时生成连续的整数值。它在常量声明中的每一行都会自动递增。
使用 iota
可以简化枚举类型的定义,避免手动为每个常量赋予递增的值。iota
的值从0开始,每出现一次,它的值就会自动递增1。
语法格式:
const (
// 枚举1、2、3可以理解为常量名
枚举1 = iota
枚举2
枚举3
// 更多的常量定义...
)
type 枚举类型 int
// 枚举1、2、3可以理解为常量名
const (
枚举1 枚举类型 = iota
枚举2
枚举3
)
iota
只能在常量声明的括号中使用,通常是与 const
一起使用。每次出现 iota
时,它的值会自动递增。
在常量声明中,可以使用 iota
来定义一组相关的常量,它们的值会自动递增。如果在同一行的常量声明中多次使用 iota
它们会共享同一个递增序列。
示例:
package main
import "fmt"
func main() {
// 口诀:iota,自动递增,每行加一,不用猜。
const (
a = iota // 这里iota的值是0
b = "hello" // b的值是hello iota=1
c // c的值默认和上一行一致是hello iota=2
d = 6.66 // d的值是6.66 iota=3
e = "world" // e的值是world iota=4
f = true // f的值是true iota=5
g = iota // g的值是6 iota=6
h // h的值是7 iota=7
)
fmt.Println(a, b, c, d, e, f, g, h)
}
package main
import "fmt"
func main() {
// iota还可以与位操作符结合使用,用于定义按位运算的常量。
const (
FlagA = 1 << iota // 1
FlagB // 2
FlagC // 4
)
fmt.Println(FlagA, FlagB, FlagC)
}
package main
import "fmt"
// 定义类型
type ByteSize float64
func main() {
// 定义数量级
const (
_ = iota // 通过分配给空白标识符来忽略第一个值
KB ByteSize = 1 << (10 * iota) // 等价于 1 << (10 * 1)
MB
GB
TB
PB
EB
ZB
YB
)
// 打印输出
fmt.Printf("%0.1f\n", KB)
fmt.Printf("%0.1f\n", MB)
fmt.Printf("%0.1f\n", GB)
fmt.Printf("%0.1f\n", TB)
fmt.Printf("%0.1f\n", PB)
fmt.Printf("%0.1f\n", EB)
fmt.Printf("%0.1f\n", ZB)
fmt.Printf("%0.1f\n", YB)
}
package main
import "fmt"
func main() {
// iota定义在一行的情况
const (
a, b = iota + 1, iota // iota = 0
c, d = iota + 1, "哈哈" // iota = 1
e, f = 55, true // iota = 2
g, h = iota, 6.666 // iota = 3
)
fmt.Println(a, b)
fmt.Println(c, d)
fmt.Println(e, f)
fmt.Println(g, h)
}
iota使用时的注意事项
- iota 的初始值为0,每次在常量声明中使用 iota 时,它的值都会自动递增。
const (
A = iota // A = 0
B // B = 1
C // C = 2
)
- iota 只在常量声明中有效,不能在其他作用域中使用。
- 在一条常量声明语句中,每次使用 iota 时,它的值都会自动递增。但是,如果中断常量声明,iota 的值不会重置。
const (
A = iota // A = 0
B // B = 1
C = 5 // C = 5
D // D = 6
)
- 在同一常量组中,可以使用空白标识符_来跳过某些值,而不让 iota 递增。
const (
A = iota // A = 0
_ // 跳过 B
C = iota // C = 2
D // D = 3
)
- iota 可以用于位运算,可以通过左移操作来生成一系列相关的位掩码常量。
const (
FlagA = 1 << iota // FlagA = 1 (1左移0位)
FlagB // FlagB = 2 (1左移1位)
FlagC // FlagC = 4 (1左移2位)
)
- 每个 const 块中的 iota 值都是独立计算的,会从 0 开始重置。
const (
A = iota // A = 0
B // B = 1
C // C = 2
)
const (
X = iota // X = 0
Y // Y = 1
Z // Z = 2
)