零基础 go - 02(变量声明和作用域)

7 阅读9分钟

一、变量声明

方式一:使用 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 来表示用户的名字。
  • 变量名应该使用驼峰式命名法,即每个单词的首字母大写,除了第一个单词。例如,userNametotalCountisValid 等。
  • 变量名应该避免使用缩写,除非缩写非常常见且易于理解。例如,使用 userID 而不是 uid 来表示用户的 ID。
  • 变量名应该避免使用单个字母,除非在特定上下文中非常常见且易于理解。例如,在循环中使用 ij 来表示索引变量是可以接受的,但在其他情况下应该使用更具描述性的变量名。
  • 变量名应该避免使用下划线,除非在特定上下文中非常常见且易于理解。例如,在测试函数中使用 TestFunctionName 是可以接受的,但在其他情况下应该使用驼峰式命名法。
  • 变量名应该避免使用 Go 语言的关键字和保留字,例如 var funcifelse 等。
  • 变量名应该避免使用过于冗长或过于简短的名称,应该在描述性和简洁性之间找到平衡。例如,使用 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,解引用指针变量
}