Golang:全局变量初始化遇到的问题记录

不说废话,直接上代码,运行结果是什么?

package main

import (
	"fmt"
)

var (
	testStruct *TestStruct
	testMap    = map[string]func(str string){
		"test": testStruct.PrintName,
	}
)

type TestStruct struct {
	Name string
	Age  int
}

func (t *TestStruct) PrintName(str string) {
	fmt.Printf("%s.%s\n", t.Name, str)
}

func main() {
	// 设置testStruct的值
	testStruct = &TestStruct{Name: "Tom", Age: 10}

	if printFunc, ok := testMap["test"]; ok {
		printFunc("Jerry")
	}
}

--------------------------------------------------分隔线-----------------------------------------------------
--------------------------------------------------分隔线-----------------------------------------------------
--------------------------------------------------分隔线-----------------------------------------------------
--------------------------------------------------分隔线-----------------------------------------------------
--------------------------------------------------分隔线-----------------------------------------------------
--------------------------------------------------分隔线-----------------------------------------------------
--------------------------------------------------分隔线-----------------------------------------------------
--------------------------------------------------分隔线-----------------------------------------------------
--------------------------------------------------分隔线-----------------------------------------------------
--------------------------------------------------分隔线-----------------------------------------------------
--------------------------------------------------分隔线-----------------------------------------------------

公布答案:

会出现空指针错误:

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x4441029]

goroutine 1 [running]:
main.(*TestStruct).PrintName(0x0, 0x45734ff, 0x5)

你知道在哪里会产生空指针错误吗?没错,就是这里:

fmt.Printf("%s.%s\n", t.Name, str)

其中,t是空,为什么呢?代码中明明在main()方法中先对testStruct变量进行了赋值,再去调用的map中的方法。按照我们的正常理解不应该出现空指针错误才对。

其实,Golang中代码运行并不完全像我们想象的那样,按照某个顺序运行,这里主要想介绍的就是Golang的全局变量初始化的一个顺序。

熟悉Go语言的都知道,全局变量、常量、init()方法这些在程序启动时有一个先后的执行顺序,当一个包被引用时,这个包内的全局变量、常量、init()方法会被执行,执行顺序为:

常量 > 全局变量 > init()方法

好的,了解以上内容后,再看代码:

var (
	testStruct *TestStruct
	testMap    = map[string]func(str string){
		"test": testStruct.PrintName,
	}
)

首先,声明了两个全局变量,其中testMap进行了初始化,并设置了key = "test",value = testStruct.PrintName,value是一个函数类型,这里指定的是testStruct的方法,他们的函数签名(指函数的参数及返回值类型、数量都一样)一致。

注意,此时testStruct还没有进行赋值,只是声明了类型,因此这个变量的值还是nil。

然后看main方法:

func main() {
	// 设置testStruct的值
	testStruct = &TestStruct{Name: "Tom", Age: 10}

	if printFunc, ok := testMap["test"]; ok {
		printFunc("Jerry")
	}
}

这里对首先对testStruct全局变量进行赋值,然后判断了一下map中是否存在key为"test",存在就调用对应的方法。

因为全局变量先于 main 方法执行,因此,testMap 的值早就存在了,这里可以获取到key="test" 的值,但是, testStruct 在map赋值的时候,是一个nil,即使在 main 方法中先进行了 testStruct 变量的赋值,map中的值也已经无法改变,这里还涉及到一个参数复制的问题,不展开讲,只需要知道map中存储的 testStruct 数据,其地址已经和声明的全局变量的 testStruct 地址完全不一样了,所以当调用 PrintName 方法时会出现空指针,那么如何避免上面的问题呢?很简单,也是利用Go语言执行顺序的机制来解决:

package main

import (
	"fmt"
)

var (
	testStruct *TestStruct
	testMap    = map[string]func(str string){}
)

func init() {
	// 设置testStruct的值
	testStruct = &TestStruct{Name: "Tom", Age: 10}

	// 设置map
	testMap = map[string]func(str string){
		"test": testStruct.PrintName,
	}
}

type TestStruct struct {
	Name string
	Age  int
}

func (t *TestStruct) PrintName(str string) {
	fmt.Printf("%s.%s\n", t.Name, str)
}

func main() {
	if printFunc, ok := testMap["test"]; ok {
		printFunc("Jerry")
	}
}

新增了一个init()方法,把testStruct的赋值从 main 方法中移到 init() 方法中,同时把testMap的赋值逻辑也放到了 init() 方法中,并且在testStrcut赋值之后。

此时,再次执行程序,会发现,如期输出:Tom.Jerry

至此,应该对 Go 语言中变量的初始化以及 init() 方法等执行顺序有了一个大致的了解了吧!也能在以后的编码中多注意如何尽量避免出现空指针,毕竟 Go 语言处理这种错误异常还是挺麻烦的。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
在Go语言中,全局变量的作用域是整个包,其他包无法直接引用main包中定义的全局变量。为了解决这个问题,可以采用以下两种方法: 1. 定义并编写单独的全局变量包:创建一个独立的包,用于存放全局变量。在该包中定义全局变量,并在其他包中引入该包来使用全局变量。 ```go package global var Gvar int ``` 在其他包中引入并使用该全局变量包: ```go package test import ( "fmt" "app/global" ) func test() { var err error global.Gvar = 2 fmt.Println(global.Gvar) } ``` ```go package main import ( "fmt" "app/global" ) var Gvar int func main() { var err error global.Gvar = 1 fmt.Println(global.Gvar) } ``` 2. 局部变量和全局变量同名:在Go语言中,全局变量与局部变量名称可以相同,但是函数内的局部变量会被优先考虑。 ```go package main import "fmt" var quanju = 10 // 全局变量 func main() { var quanju = 100 // 局部变量 fmt.Println("quanju=", quanju) // 输出局部变量的值 } ``` 总结:在Go语言中,全局变量的作用域是整个包,其他包无法直接引用main包中定义的全局变量。可以通过定义单独的全局变量包或使用同名的局部变量来解决这个问题。 #### 引用[.reference_title] - *1* [golang关于全局变量的使用和理解](https://blog.csdn.net/itopit/article/details/127469006)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [Golang开发--全局变量/局部变量](https://blog.csdn.net/liulanba/article/details/115769826)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值