全局空间中的空指针异常

huangapple go评论77阅读模式
英文:

Nil Pointer Exception in Global space

问题

我正在尝试理解指针的概念。

我有三个包:main、models和controller

在我的models.go文件中:

type Database struct {
	config 	string
	port 	int
}

var DB *Database

func GetDatabase() *Database {
	fmt.Println("Reporting from Models.")
	DB = &Database{config: "This is stunning!", port: 3306}
	fmt.Printf("%v, %T\n", DB, DB)
	return DB
}

controller.go文件中:

var DB *models.Database = models.DB

func GetController() {
	fmt.Println("Reporting from Controllers!")
	fmt.Printf("%v, %T\n", DB, DB)
}

main.go文件中:

var DB *models.Database

func main() {
	DB = models.GetDatabase()
	controllers.GetController()
	fmt.Println("Reporting from main")
	fmt.Printf("%v, %T\n", DB, DB)
}

输出结果

Reporting from Models.
&{This is stunning! 3306}, *models.Database
Reporting from Controllers!
<nil>, *models.Database
Reporting from main
&{This is stunning! 3306}, *models.Database

我的问题是为什么在我的控制器中,DB变量的值是nil?我的直觉是它在models包内访问了全局的DB变量,而该变量是nil,因为它没有被初始化。但是,既然它是一个引用,并且我正在尝试在GetDatabase函数内给它一个正确的引用,为什么在我尝试在控制器内访问该变量时,这个变化没有反映出来呢?

英文:

I'm trying to understand the concept of pointers.

I have three packages main, models and controller

in my models.go

type Database struct {
	config 	string
	port 	int
}

var DB *Database

func GetDatabase() *Database {
	fmt.Println(&quot;Reporting from Models.&quot;)
	DB = &amp;Database{config: &quot;This is stunning!&quot;, port: 3306}
	fmt.Printf(&quot;%v, %T\n&quot;, DB, DB)
	return DB
}

in controller.go

var DB *models.Database = models.DB

func GetController() {
	fmt.Println(&quot;Reporting from Controllers!&quot;)
	fmt.Printf(&quot;%v, %T\n&quot;, DB, DB)
}

in main.go

var DB *models.Database

func main() {
	DB = models.GetDatabase()
	controllers.GetController()
	fmt.Println(&quot;REporting from main&quot;)
	fmt.Printf(&quot;%v, %T\n&quot;, DB, DB)
}

The OUTPUT

Reporting from Models.
&amp;{This is stunning! 3306}, *models.Database
Reporting from Controllers!
&lt;nil&gt;, *models.Database
REporting from main
&amp;{This is stunning! 3306}, *models.Database

My question here is why am I getting nil inside my controller for DB? My intuition is that its accessing the global DB variable at package level inside models which is nil since its not initiated. But since its a reference and I'm trying to give it a proper reference inside my GetDatabase function why is that change not getting reflected when I try to access that variable inside my controller?

答案1

得分: 1

如果你想分享一些内容,你需要一个指针,但不是一个nil指针,而是一个实际分配的指针。然后你还需要使用解引用来更新共享指针的每个实例,基本上解引用是指你如何更新存储在指针所指向的地址上的数据。

var DB = &Database{}

func GetDatabase() *Database {
    // 在表达式前面加上一个星号是解引用指针的方法
    *DB = Database{config: "This is stunning!", port: 3306}
}
var DB = models.DB

func GetController() {
    fmt.Printf("%v, %T\n", DB, DB) // 不再是nil
}
var DB = models.DB

func main() {
    // 你可以省略赋值操作,因为GetDatabase()会更新models.DB的共享指针
    _ = models.GetDatabase()
    fmt.Println(DB)
}
英文:

If you want to share something you need a pointer, but NOT a nil pointer, an actual allocated one. Then you also need to use dereferencing to be able to update every instance of the shared pointer, basically dereferencing is how you update data stored at the address to which the pointer points.

var DB = &amp;Database{}

func GetDatabase() *Database {
    // an asterisk before an expression is how you dereference a pointer
    *DB = Database{config: &quot;This is stunning!&quot;, port: 3306}
}
var DB = models.DB

func GetController() {
    fmt.Printf(&quot;%v, %T\n&quot;, DB, DB) // not nil anymore
}
var DB = models.DB

func main() {
    // you can ommit the assignment since GetDatabase() updates the shared 
    // pointer of models.DB
    _ = models.GetDatabase()
    fmt.Println(DB)
}

答案2

得分: 1

指针本质上是整数,它们存储的数字是内存中某个其他变量的地址。如果你不熟悉指针,但是熟悉具有数组的语言,可以将指针视为保存数组索引的整数,其中数组就是内存。

当你运行像这样的语句时...

package controllers

var DB *models.Database = models.DB

实际上你在这里做的是整数赋值,将models.DB的值按值复制到变量controllers.DB中。在执行这行代码的时候,models.DB的值是nil,所以你将地址nil复制到controllers.DB中。

通过这种按值复制,对models.DB变量的任何更改都与controllers.DB变量完全解耦。它们是两个独立的东西,它们都指向nil。现在,当你将某个实际DB实例的地址分配给models.DB时,models.DB的值会改变,但是不相关的变量controllers.DB不受影响。

一个变量的更改(即赋值)不会被镜像到其他变量上。但是,如果两个指针指向同一块内存,并且你更改了内存本身,那么这些更改将通过两个指针都可见。

如果你这样做...

models.DB = &Database{} // 非nil地址
controllers.DB = models.DB // 复制非nil地址

那么两个变量将包含相同的地址,并且对它们现在都指向的单个Database变量的更改将通过两个指针都可见。然而,重新分配变量本身,即models.DB = nil,不会影响另一个变量。

回到数组的例子,你实际上做了这个:

arr := []string{"zero", "one", "two", "three"}

idx1 := 0 // 即models.DB
idx2 := 0 // 即controllers.DB

idx1 = idx2 // 按值将0复制到idx1

idx2 = 1 // 不会影响仍为0的idx1

arr[idx1] // "zero"
arr[idx2] // "one"
英文:

Pointers are essentially integers, where the number they store is the address of some other variable in memory. If you're not used to pointers, but come from a language that has arrays, it's more or less correct to think of pointers as integers holding array indexes, where the array is memory.

When you run a statement like, this....

package controllers

var DB *models.Database = models.DB

What you're really doing here is effectively integer assignment, where the value of models.DB is copied by value into the variable controllers.DB. At the point in time where this line of code is executed, the value of models.DB is nil, so you copy the address nil into controllers.DB.

After this copy-by-value, any changes to the models.DB variable are completely decoupled from the controllers.DB variable. They're two separate things, and they both point to nil. Now, when you assign address of some actual DB instance to models.DB, the value of models.DB changes, but the unrelated variable controllers.DB is unaffected.

Changes to one variable (ie assignment) don't get mirrored to other variables. But, if two pointers point to the same memory, and you change the memory itself, then the changes are visible through both pointers.

If you did this...

models.DB = &amp;Database{} // non-nil address
controllers.DB = models.DB // non-nil address is copied

then both variable would contain the same address, and changes to the single Database variable they both now point to would be visible through both pointers. However, reassigning the variable itself, ie models.DB = nil, will not affect the other variable.

Going back to the array example, you've effectively done this:

arr := []string{&quot;zero&quot;, &quot;one&quot;, &quot;two&quot;, &quot;three&quot;}

idx1 := 0 // ie models.DB
idx2 := 0 // ie controllers.DB

idx1 = idx2 // copy 0 by value into idx1

idx2 = 1 // does not affect idx1, which is still 0

arr[idx1] // &quot;zero&quot;
arr[idx2] // &quot;one&quot;

huangapple
  • 本文由 发表于 2021年7月15日 03:03:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/68383788.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定