附加到结构切片时无效的内存地址或 nil 指针取消引用

Posted

技术标签:

【中文标题】附加到结构切片时无效的内存地址或 nil 指针取消引用【英文标题】:Invalid memory address or nil pointer dereference when appending to slice of structs 【发布时间】:2014-02-23 18:08:51 【问题描述】:
package main

import (
    "fmt"
)

type Person struct 
    name    string


func main() 
    p := make([]*Person, 0)
    p = append(p, &Person"Brian")
    fmt.Println(p[0].name)
    p = append(p, &Person"Le Tu")
    fmt.Println(p[1].name)

以上工作正常。

package main

import (
    "fmt"
)

type Person struct 
    name    string


func main() 
    p := make([]*Person, 1) //Changed to 1 instead of 0
    p = append(p, &Person"Brian")
    fmt.Println(p[0].name)
    p = append(p, &Person"Le Tu")
    fmt.Println(p[1].name)

上述恐慌。

我对@9​​87654323@ 的理解是它隐藏了扩展/添加的机制。显然,我使用append 作为切片的一种“推动”的心理模型是不正确的。谁能向我解释为什么上面的第二个样本会出现恐慌?为什么我不能只 append 我的结构?

【问题讨论】:

【参考方案1】:

Reference page for make

使用make构建切片时,第一个整数参数是创建切片的实际长度:

p := make([]*Person, 1)
// is equivalent to
p := []*Personnil  //<- slice of length 1, cells contain the default
                     //   zero value for the *Person type

如果你想创建一个长度为 0 的切片,但具有预定义的 容量,你需要使用 make 的 3 参数版本:

p := make([]*Person, 0, 100) //<- slice of length 0, but the first 100 append 
                             //   won't reallocate the slice

关于如何使用这两种情况的简单示例:

//100-cell slice :
p1 := make([]*Person, 100)
for i := 0; i < 100; i++ 
    name := fmt.Sprintf("Foo %d", i+1)
    //you can access and assign to p1[i] because p1 has 100 cells :
    p1[i] = &Personname

fmt.Printf("p1[0].Name : %s\n", p1[0].Name)
fmt.Printf("p1[99].Name : %s\n", p1[99].Name)

//0-cell, 100-capacity slice :
p2 := make([]*Person, 0, 100)
for i := 0; i < 100; i++ 
    name := fmt.Sprintf("Foo %d", i+1)
    //you cannot access p2[i], the cell doesn't exist yet :
    p2 = append(p2, &Personname)

fmt.Printf("p2[0].Name : %s\n", p2[0].Name)
fmt.Printf("p2[99].Name : %s\n", p2[99].Name)

play.golang link

【讨论】:

【参考方案2】:

例如,

package main

import (
    "fmt"
)

type Person struct 
    name string


func main() 
    p := make([]*Person, 1) //Changed to 1 instead of 0
    fmt.Println(len(p), p)
    p = append(p, &Person"Brian")
    fmt.Println(len(p), p)
    fmt.Println(p[1].name)
    fmt.Println(p[0])
    fmt.Println(p[0].name)

输出:

1 [<nil>]
2 [<nil> 0x10500190]
Brian
<nil>
panic: runtime error: invalid memory address or nil pointer dereference

p 在附加前的长度为 1,在附加后的长度为 2。所以p[0]有未初始化的指针值nilp[0].name无效。

The Go Programming Language Specification

Appending to and copying slices

可变参数函数 append 将零个或多个值 x 附加到 s 的 类型 S,必须是切片类型,并返回结果切片, 也是S型。

Pointer types

未初始化的指针的值为 nil。

Selectors

以下规则适用于选择器:

4) 如果 x 是指针类型且值为 nil 且 x.f 表示结构字段,则分配或评估 x.f 会导致运行时恐慌。

【讨论】:

以上是关于附加到结构切片时无效的内存地址或 nil 指针取消引用的主要内容,如果未能解决你的问题,请参考以下文章

使用 mgo 的无效内存地址或 nil 指针取消引用

为啥 gorm db.First() 会因“无效的内存地址或 nil 指针取消引用”而恐慌? [复制]

无法生成 ngrokpanic:运行时错误:无效的内存地址或 nil 指针取消引用

k6:WARpanic:运行时错误:无效的内存地址或 nil 指针取消引用

golang - mysql 恐慌:运行时错误:无效的内存地址或 nil 指针取消引用

sql golang查询时,无效的内存地址或nil指针取消引用。