一、变量声明
方式一:使用 var 声明变量
package main
import "fmt"
func main() {
var a1
var a, b
fmt.Println(a1) // 输出 0
fmt.Println(a) // 输出 0
fmt.Println(b) // 输出 ""
}
方式一:使用 var 声明变量并指定类型
package main
import "fmt"
func main() {
var a1 number
var a number, b string
fmt.Println(a1) // 输出 0
fmt.Println(a) // 输出 0
fmt.Println(b) // 输出 ""
}
方式三:使用 var 声明变量并指定初始值
package main
import "fmt"
func main() {
var a1 = 10
var a2 number = 10 // 通常会省略类型,直接写成 var a2 = 10,编译器会自动推断类型
var a, b = 10, "hello"
fmt.Println(a1) // 输出 10
fmt.Println(a2) // 输出 10
fmt.Println(a) // 输出 10
fmt.Println(b) // 输出 "hello"
}
方式四:使用简短变量声明(注意不能使用 var)
package main
import "fmt"
func main() {
a1 := 10 // 相当于 var a1: int a1 = 10
a, b := 10, "hello"
fmt.Println(a1) // 输出 10
fmt.Println(a) // 输出 10
fmt.Println(b) // 输出 "hello"
}
二、全局变量
全局变量必须使用 var 声明,不能使用简短变量声明 全局变量可以在函数外部声明,并且在整个包内都可见
单变量声明
package main
import "fmt"
var global_a = 10
func main() {
fmt.Println(global_a) // 输出 10
}
多变量声明
package main
import "fmt"
var global_a = 10
var global_b = "hello"
/*
上面可以简写为
var (
global_a = 10
global_b = "hello"
)
*/
func main() {
fmt.Println(global_a) // 输出 10
fmt.Println(global_b) // 输出 "hello"
}
全局变量的零值
全局变量如果没有显式初始化,会被赋予零值。不同类型的零值如下:
- 数值类型(如 int、float64)的零值为 0
- 字符串类型的零值为 ""
- 布尔类型的零值为 false
- 指针、切片、映射、通道、接口等引用类型的零值为 nil
package main
import "fmt"
var global_a int
var global_b string
/*
上面可以简写为
var (
global_a int
global_b string
)
*/
func main() {
fmt.Println(global_a) // 输出 0
fmt.Println(global_b) // 输出 ""
}
三、不同类型的零值
变量如果没有显式初始化,会被赋予零值。不同类型的零值如下:
- 数值类型(如 int、float64)的零值为 0
- 字符串类型的零值为 ""
- 布尔类型的零值为 false
- 指针、切片、映射、通道、接口等引用类型的零值为 nil
四、变量的作用域
变量的作用域是指变量在程序中可见和可访问的范围
局部变量的作用域
局部变量是在函数内部声明的变量,它们只能在函数内部访问,函数执行结束后局部变量就会被销毁。例如:
package main
import "fmt"
func main() {
var local_a = 10
fmt.Println(local_a) // 输出 10
}
全局变量的作用域
全局变量是在函数外部声明的变量,它们在整个包内都可见和可访问。例如:
package main
import "fmt"
var global_a = 10
func main() {
fmt.Println(global_a) // 输出 10
}
块级作用域
块级作用域是指在代码块(如 if、for、switch 等)内部声明的变量,它们只能在该代码块内部访问。例如:
package main
import "fmt"
func main() {
if true {
var block_a = 10
fmt.Println(block_a) // 输出 10
}
// fmt.Println(block_a) // 编译错误,block_a 在 if 块外不可见
}
五、变量遮蔽
变量遮蔽是指在内层作用域中声明一个与外层作用域同名的变量,这会导致内层变量遮蔽外层变量,使得在内层作用域中访问该变量时,实际上访问的是内层变量而不是外层变量。例如:
package main
import "fmt"
var a = 10
func main() {
fmt.Println(a) // 输出 10
{
var a = 20 // 这里的 a 遮蔽了外层的 a
fmt.Println(a) // 输出 20
}
fmt.Println(a) // 输出 10,外层的 a 仍然可见
}
六、变量的生命周期
变量的生命周期是指变量从创建到销毁的过程。
- 局部变量的生命周期从函数调用开始,到函数执行结束为止;
- 全局变量的生命周期从程序开始运行到程序结束为止;
- 块级变量的生命周期从进入代码块开始,到离开代码块为止。
- 变量的生命周期决定了变量的内存分配和释放方式,以及变量 的可访问性和作用域范围。
七、变量的内存分配和释放
- 局部变量通常分配在栈上,函数调用结束后会自动释放;
- 全局变量和引用类型的变量通常分配在堆上,程序结束时由垃圾回收器自动释放;
- 块级变量的内存分配和释放方式与局部变量类似,但它们的生命周期更短。
八、变量的可访问性和作用域范围
变量的可访问性和作用域范围由其声明的位置决定。
- 局部变量只能在函数内部访问
- 全局变量在整个包内都可见
- 块级变量只能在代码块内部访问
- 变量遮蔽可能会导致意外的行为,因此在编写代码时应避免使用同名变量,或者在必要时使用不同的命名来区分变量的作用域
九、变量的命名规范
- 变量名应该具有描述性,能够清晰地表达变量的用途和含义。例如,使用
userName而不是u来表示用户的名字。 - 变量名应该使用驼峰式命名法,即每个单词的首字母大写,除了第一个单词。例如,
userName、totalCount、isValid等。 - 变量名应该避免使用缩写,除非缩写非常常见且易于理解。例如,使用
userID而不是uid来表示用户的 ID。 - 变量名应该避免使用单个字母,除非在特定上下文中非常常见且易于理解。例如,在循环中使用
i、j来表示索引变量是可以接受的,但在其他情况下应该使用更具描述性的变量名。 - 变量名应该避免使用下划线,除非在特定上下文中非常常见且易于理解。例如,在测试函数中使用
TestFunctionName是可以接受的,但在其他情况下应该使用驼峰式命名法。 - 变量名应该避免使用 Go 语言的关键字和保留字,例如
var、func、if、else等。 - 变量名应该避免使用过于冗长或过于简短的名称,应该在描述性和简洁性之间找到平衡。例如,使用
userName而不是theNameOfTheUser来表示用户的名字。 - 变量名应该遵循一致的命名风格,在整个代码库中保持一致。例如,如果在一个项目中使用驼峰式命名法,那么在整个项目中都应该使用驼峰式命名法来命名变量。
十、变量赋值注意事项
变量赋值的注意事项总结:
1. 变量必须先声明后赋值。
2. 变量赋值可以在声明时进行,也可以在声明后进行。
3. 变量赋值可以使用简短变量声明(:=)来同时声明和赋值。
4. 变量赋值可以是基本类型的值,也可以是复合类型的值。
5. 变量赋值可以是一个表达式的结果,例如函数调用、算术运算等。
6. 变量赋值可以是多个变量同时赋值。
7. 变量赋值可以是一个变量赋值给另一个变量。
8. 变量赋值可以是一个变量的地址赋值给一个指针变量。
9. 变量赋值时要注意类型匹配,确保赋值的值与变量的类型兼容。
10. 变量赋值时要注意变量的作用域,确保在正确的作用域内进行赋值。
11. 变量赋值时要注意变量的生命周期,确保在变量有效的生命周期内进行赋值。
12. 变量赋值时要注意变量的命名规范,使用描述性和一致的变量名来提高代码的可读性和维护性。
- 变量赋值是将一个值赋给一个变量,变量必须先声明后赋值。例如:
package main
import "fmt"
func main() {
var a int
a = 10 // 先声明变量 a,然后赋值
fmt.Println(a) // 输出 10
}
- 变量赋值可以在声明时进行,也可以在声明后进行。例如:
package main
import "fmt"
func main() {
var a = 10 // 声明变量 a 并赋值
var b int
b = 20 // 声明变量 b 后赋值
fmt.Println(a) // 输出 10
fmt.Println(b) // 输出 20
}
- 变量赋值可以使用简短变量声明(:=)来同时声明和赋值。例如:
package main
import "fmt"
func main() {
a := 10 // 使用简短变量声明同时声明和赋值
b := 20
fmt.Println(a) // 输出 10
fmt.Println(b) // 输出 20
}
- 变量赋值可以是基本类型的值,也可以是复合类型的值,例如数组、切片、结构体等。例如:
package main
import "fmt"
type Person struct {
Name string
Age int
}
func main() {
var a int
a = 10 // 基本类型赋值
var b []int
b = []int{1, 2, 3} // 切片赋值
var p Person
p = Person{Name: "Alice", Age: 30} // 结构体赋值
fmt.Println(a) // 输出 10
fmt.Println(b) // 输出 [1 2 3]
fmt.Println(p) // 输出 {Alice 30}
}
- 变量赋值可以是一个表达式的结果,例如函数调用、算术运算等。例如:
package main
import "fmt"
func add(x, y int) int {
return x + y
}
func main() {
var a int
a = add(10, 20) // 函数调用赋值
var b int
b = a * 2 // 算术运算赋值
fmt.Println(a) // 输出 30
fmt.Println(b) // 输出 60
}
- 变量赋值可以是多个变量同时赋值,例如:
package main
import "fmt"
func main() {
var a, b int
a, b = 10, 20 // 多变量同时赋值
fmt.Println(a) // 输出 10
fmt.Println(b) // 输出 20
}
- 变量赋值可以是一个变量赋值给另一个变量,例如:
package main
import "fmt"
func main() {
var a int
a = 10
var b int
b = a // 变量赋值给另一个变量
fmt.Println(a) // 输出 10
fmt.Println(b) // 输出 10
}
- 变量赋值可以是一个变量的地址赋值给一个指针变量,例如:
package main
import "fmt"
func main() {
var a int
a = 10
var p *int
p = &a // 变量地址赋值给指针变量
fmt.Println(a) // 输出 10
fmt.Println(p) // 输出 a 的地址
fmt.Println(*p) // 输出 10,解引用指针变量
}