英文:
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("Reporting from Models.")
DB = &Database{config: "This is stunning!", port: 3306}
fmt.Printf("%v, %T\n", DB, DB)
return DB
}
in controller.go
var DB *models.Database = models.DB
func GetController() {
fmt.Println("Reporting from Controllers!")
fmt.Printf("%v, %T\n", DB, DB)
}
in 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)
}
The OUTPUT
Reporting from Models.
&{This is stunning! 3306}, *models.Database
Reporting from Controllers!
<nil>, *models.Database
REporting from main
&{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 = &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)
}
答案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 = &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{"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"
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论