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

时间:2014-01-31 05:58:39

标签: struct go slice

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)
}

以上恐慌。

我对append的理解是它隐藏了扩展/添加的机制。很明显,我使用append作为切片“推”的心理模型是不正确的。任何人都可以向我解释为什么上面的第二个样本恐慌?为什么我不能append我的结构?

2 个答案:

答案 0 :(得分:5)

例如,

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]具有未初始化的指针值nil,而p[0].name无效。

  

The Go Programming Language Specification

     

Appending to and copying slices

     

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

     

Pointer types

     

未初始化指针的值为nil。

     

Selectors

     

以下规则适用于选择器:

     

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

答案 1 :(得分:2)

Reference page for make

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

p := make([]*Person, 1)
// is equivalent to
p := []*Person{nil}  //<- 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] = &Person{name}
}
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, &Person{name})
}
fmt.Printf("p2[0].Name : %s\n", p2[0].Name)
fmt.Printf("p2[99].Name : %s\n", p2[99].Name)

play.golang link