Golang新手问题:变量初始化的方法与技巧

Golang新手问题:变量初始化的方法与技巧 大家好,我正在学习Golang,有个问题想请教。

这是一个简单的测试程序代码:

package main
import ("fmt"
)

var (
john string
mike string
c = "dce"
)

func main() {

john := "john"
c = "A"
c := "B"
i := 10

fmt.Println("i",i,john,c)

c := "C"
}

请注意变量’c’。 我先给它赋值为"A",然后使用:=运算符声明并设置为"B" 代码运行没有任何错误,但当我再次使用:=将其设置为"C"时,Golang就会报错。 难道在尝试将其设置为"B"时不应该报错吗?因为c已经被赋值过了。

我原以为:=只在变量尚未初始化时使用,所以产生了这个疑问。


更多关于Golang新手问题:变量初始化的方法与技巧的实战教程也可以访问 https://www.itying.com/category-94-b0.html

8 回复

感谢 @NoobZ@Yamil_Bracho 的回复!

更多关于Golang新手问题:变量初始化的方法与技巧的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


:= 只有在当前作用域中已存在该变量时才会失效,但它会遮蔽来自更大/外部作用域的变量。

霍利韦,

非常感谢!这解释并解决了我的问题!也非常感谢您提供的示例代码,让我一步步理解……我现在变得更好了 :-)。

好的,但我觉得这有点奇怪。我认为不管变量是全局还是局部的,根据 := 的定义,编译器应该报错才对,你不觉得吗?不确定允许这种情况发生有什么好处……

“:=” 用于声明并初始化变量,它仅在函数作用域内有效。

因此,如果你这样写:

var a bool = true

等同于这样写:

a := true

非常感谢!这解释并解决了我的问题!同时也非常感激您提供的示例代码,让我能够逐步理解……我现在进步了很多 :-)。

不客气!记得也要感谢 @NobbZ@Yamil_Bracho!在结束之前:

欢迎来到 Golang Bridge!🚀🎆

NobbZ:

只有当变量在当前作用域中已存在时,:= 才会报错,但它会遮蔽来自更大/外部作用域的变量。

howardyoo:

好吧,但这有点奇怪,我认为无论变量来自全局还是局部作用域,根据 := 的定义,编译器都应该报错,你不觉得吗?不确定允许这种情况发生有什么好处…

简单来说,你遇到了使用全局变量的后果。在 Go 语言中,允许声明与全局变量同名的局部变量,但会失去访问对应全局变量的能力。

参考以下代码:

package main

import (
	"fmt"
)
var (
	a = "Hello"
	b = "World"
)
func globalCheck() {
	fmt.Printf("GLOBAL: %v %v\n", a, b)
}
func main() {
	fmt.Printf("AFTER : %v %v\n", a, b)
	globalCheck()
}
// 输出:
// AFTER : Hello World
// GLOBAL: Hello World

这里你访问的是两个全局变量。现在,在 main 函数内部,我们声明另一个局部变量 a

package main
import (
	"fmt"
)
var (
	a = "Hello"
	b = "World"
)
func globalCheck() {
	fmt.Printf("GLOBAL: %v %v\n", a, b)
}
func main() {
	a := "Bonjour"
	fmt.Printf("AFTER : %v %v\n", a, b)
	globalCheck()
}
// 输出:
// AFTER : Bonjour World
// GLOBAL: Hello World

如果在 main 函数中修改变量 a,你设置的是局部版本:

package main
import (
	"fmt"
)
var (
	a = "Hello"
	b = "World"
)
func globalCheck() {
	fmt.Printf("GLOBAL: %v %v\n", a, b)
}
func main() {
	a := "Bonjour"
	fmt.Printf("BEFORE: %v %v\n", a, b)
	a = "你好"
	fmt.Printf("AFTER : %v %v\n", a, b)
	globalCheck()
}
// 输出:
// BEFORE: Bonjour World
// AFTER : 你好 World
// GLOBAL: Hello World

因此,如果在 main 函数内部声明局部变量 a 后再次使用 := 运算符,会导致编译错误,提示 a 已存在:

package main
import (
	"fmt"
)
var (
	a = "Hello"
	b = "World"
)
func globalCheck() {
	fmt.Printf("GLOBAL: %v %v\n", a, b)
}
func main() {
	a := "Bonjour"
	fmt.Printf("BEFORE: %v %v\n", a, b)
	a := "你好"
	fmt.Printf("AFTER : %v %v\n", a, b)
	globalCheck()
}
// 输出:
// # command-line-arguments
// ./main.go:19:4: no new variables on left side of :=

如何重新访问全局变量

  1. 首先不要使用全局变量(推荐)
  2. 避免命名冲突。注意你无法阻止使用者重复这个问题
  3. 使用 setter/getter 函数。例如 globalCheck() 就是一个 getter 函数

在您的代码中,变量 c 的行为涉及 Go 语言中变量声明和短变量声明(:=)的作用域规则。让我们逐步分析:

  1. 全局变量 c

    • 在包级别,var c = "dce" 声明了一个全局变量 c,初始值为 "dce"
    • 全局变量在包内所有函数中可见。
  2. main 函数内

    • c = "A":这是对全局变量 c 的赋值,将值从 "dce" 改为 "A"
    • c := "B":这里使用短变量声明 :=。在 Go 中,:= 会声明一个新变量,如果当前作用域内已有同名变量,它会创建一个新的局部变量,隐藏外部变量。因此,这一行声明了一个新的局部变量 c,初始化为 "B",并隐藏了全局变量 c
    • c := "C":当您再次使用 := 声明 c 时,由于局部变量 c 已在上一步声明(通过 c := "B"),在同一作用域内重复声明会导致编译错误。Go 不允许在同一作用域内重复声明变量(使用 := 或其他方式)。

关键点

  • 短变量声明 := 仅在变量在当前作用域内未声明时才用于声明新变量。如果变量已存在,:= 会尝试重新声明,但仅当至少有一个新变量被声明时才允许(即左侧变量列表中至少有一个是新的)。在您的代码中,c := "C" 没有新变量,因此报错。
  • c := "B" 时没有报错,因为它创建了一个新局部变量,隐藏了全局 c。但 c := "C" 在同一作用域内重复声明同一变量,违反了规则。

示例代码解释: 这是您的代码简化后,展示作用域问题:

package main
import "fmt"

var c = "dce" // 全局变量

func main() {
    c = "A"       // 赋值给全局变量 c
    fmt.Println(c) // 输出 "A"

    c := "B"      // 声明新局部变量 c,隐藏全局 c
    fmt.Println(c) // 输出 "B"

    // c := "C"   // 如果取消注释,会报错:no new variables on left side of :=
    // 因为 c 已在同一作用域声明

    // 要修改局部 c,直接赋值:
    c = "C"       // 正确:赋值给局部变量 c
    fmt.Println(c) // 输出 "C"
}

输出

A
B
C

总结

  • 使用 := 时,确保在当前作用域内没有重复声明同一变量。
  • 如果变量已存在,使用简单赋值 = 来修改值。
  • 全局变量和局部变量可能因作用域隐藏而表现不同;使用短变量声明时要小心作用域变化。

在您的原始代码中,c := "B" 是允许的,因为它引入了新局部变量,但 c := "C" 在同一块中重复声明,导致错误。修正方法是使用 c = "C" 进行赋值。

回到顶部