全局 space 中的 Nil 指针异常

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?我的直觉是它在模型内部的包级别访问全局 DB 变量,这是 nil 因为它没有启动。但是既然它是一个引用,我正试图在我的 GetDatabase 函数中给它一个正确的引用,为什么当我尝试访问我的控制器中的那个变量时,这个变化没有得到反映?

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

var DB = &Database{}

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

func GetController() {
    fmt.Printf("%v, %T\n", 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)
}

指针本质上是整数,它们存储的数字是内存中某个其他变量的地址。如果您不习惯指针,但来自一种具有数组的语言,那么将指针视为保存数组索引的整数或多或少是正确的,其中数组是内存。

当你运行这样的陈述时,这个....

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{} // non-nil address
controllers.DB = models.DB // non-nil address is copied

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

回到数组示例,您已经有效地做到了这一点:

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

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] // "zero"
arr[idx2] // "one"