Pointers 为什么我不能将值附加到结构';使用引用进行切片?

Pointers 为什么我不能将值附加到结构';使用引用进行切片?,pointers,go,slice,Pointers,Go,Slice,我假设切片是通过引用传递的,但这似乎适用于值 但不适用于阵列本身。例如,如果我有这个结构: l := Line{ Points: []Point{ Point{3, 4}, }, } 我可以定义一个变量,该变量被传递到结构的切片的引用 slice := l.Points 如果我修改它,变量引用的原始结构 将反映这些修改 slice[0].X = 1000 fmt.Printf( "This value %d is

我假设切片是通过引用传递的,但这似乎适用于值 但不适用于阵列本身。例如,如果我有这个结构:

    l := Line{
        Points: []Point{
            Point{3, 4},
        },
    }
我可以定义一个变量,该变量被传递到结构的切片的引用

slice := l.Points
如果我修改它,变量引用的原始结构 将反映这些修改

slice[0].X = 1000
fmt.Printf(
    "This value %d is the same as this %d", 
    slice[0].X, 
    l.Points[0].X,
)
这与数组的行为不同,我假设数组是按值传递的。 例如,如果我使用数组定义了前面的代码:

l := Line{
    Points: [1]Point{
        Point{3, 4},
    },
}
arr := l.Points
arr[0].X = 1000
fmt.Println(arr.[0].X != s.Points[0].X) // equals true, original struct is untouched
这样,就不会修改
l
结构

现在,如果我想修改切片本身,我显然不能这样做:

slice = append(slice, Point{99, 100})
因为这只会重新定义slice变量,从而丢失原始引用。 我知道我可以做到这一点:

l.Points = append(l.Points, Point{99, 100})
但是,在某些情况下,使用另一个变量比使用 把整件事都打出来

我试过这个:

*slice = append(*slice, Point{99, 100})
slice := &l.Points

*slice = append(l.Points, Point{99, 100})
但它不起作用,因为我试图去引用一些显然不是指针的东西

我终于试过了:

*slice = append(*slice, Point{99, 100})
slice := &l.Points

*slice = append(l.Points, Point{99, 100})

这是可行的,但我不确定发生了什么。为什么切片的值没有被覆盖?
append
在这里是如何工作的?

append返回一个新切片,该切片可能会修改初始切片的原始备份数组。原始片仍将指向原始备份阵列,而不是新的备份阵列(可能在内存中的同一位置,也可能不在内存中的同一位置)

例如()

虽然切片可以描述为“指向数组的胖指针”,但它不是指针,因此不能取消对它的引用,这就是为什么会出现错误

通过创建指向切片的指针,并像上面那样使用
append
,可以将指针指向的切片设置为
append
返回的“新”切片


有关详细信息,请查看您的第一次尝试失败,因为切片不是指针,它们可以被视为引用类型。如果Append有足够的容量,它将修改底层数组,否则它将返回一个新的切片

你可以通过两次尝试的结合来实现你想要的

l:=行{
点数:[]点数{
第{3,4}点,
},
}
切片:=&l.点
对于i:=0;i<100;i++{
*切片=追加(*切片,点{99+i,100+i})
}
fmt.Println(l.点)

我们先不提术语问题。该词的使用方式与您使用该词的方式不同。然而Go确实有指针,指针是一种引用形式。此外,切片和映射有点特殊,因为对于某些类型的T或类型对T1和T2.1,切片下的数组或映射的存储可能已经存在,也可能不存在,或者通过声明或定义一个类型为
slice of T
map[T1]T2
的变量来创建

我们可以将您在谈论时使用的单词reference表示显式指针,例如:

func f1(p *int) {
    // code ...
}
以及在谈论以下内容时隐含的指针:

func f2(m map[T1]T2) { ... }
func f3(s []T) { ... }
f1
中,
p
实际上是一个指针:因此它指的是一些实际的
int
,或者是
nil
。在
f2
中,
m
指的是一些底层映射,或者是
nil
。在
f3
中,
s
指的是一些底层数组,或者是
nil

但如果你写:

那么你一定写过:

type Line struct {
    // ... maybe some fields here ...
    Points []Point
    // ... maybe more fields here ...
}
是一种结构类型。它不是切片类型;它不是地图类型。它包含一个切片类型,但它本身不是切片类型

你现在谈论的是传递这些切片。如果传递
l
,则按值传递整个
struct
。区分这一点和传递
l.Points
的值非常重要。接收其中一个参数的函数必须使用正确的类型声明它

因此,在大多数情况下,谈论参考文献只是一种转移注意力的手段——从实际发生的事情中转移注意力。我们需要知道的是:您分配了哪些变量值,使用了哪些源代码

抛开这些,让我们谈谈您的实际代码示例:

这正是它所说的:

  • l.Points
    传递到
    append
    ,这是一个内置的,因为它在某种程度上具有神奇的类型灵活性(与Go的其他部分相比,Go的类型非常严格)。它接受类型为
    []T
    的任何值(T的一部分,用于任何有效类型T)加上类型为
    T
    的一个或多个值,并生成相同类型的新值
    []T

  • 将结果分配给
    l.Points

append
执行其工作时,它可能:

  • receive
    nil
    (给定类型):在本例中,它创建基础数组,或
  • 接收一个非nil片:在这种情况下,它会写入底层数组或丢弃该数组,以便根据需要使用新的更大容量数组。2
因此,在所有情况下,底层数组实际上都可能刚刚创建或替换。因此,适当地更新同一底层数组的任何其他用法都很重要。将结果赋回
l.Points
将更新引用基础数组的唯一切片变量

然而,我们可以打破这些假设:

s2 := l.Points
现在
l.点
s2
都指向(单个)底层数组。修改基础数组的操作将至少潜在地影响
s2
l.Points

您的第二个示例本身就可以:

但是您还没有展示如何声明和/或分配
切片
本身

您的第三个示例也很好:

其中第一行声明并初始化
slice
以指向
l.Points
。因此,变量
slice
具有类型
*[]点<
l.Points = append(l.Points, Point{99, 100})
s2 := l.Points
*slice = append(*slice, Point{99, 100})
slice := &l.Points
*slice = append(l.Points, Point{99, 100})
*slice = append(*slice, Point{99, 100})
l.Points = append(*slice, Point{99, 100})
var s []int
var s = []int{42}
type Slice struct {
    len int
    cap int
    Array *[n]T // Pointer to array of type T
} 
var s []int
s[0] = 1
var s Slice
*s.Array[0] = 1 
myArray := [3]int{1,1,1}
mySlice := myArray[0:1]
mySlice = append(mySlice, 2, 3) // myArray == mySlice
myArray := [3]int{1,1,1}
mySlice := myArray[0:1]
mySlice = append(mySlice, 2, 3, 4, 5) // myArray != mySlice
sliceCopy := mySlice
sliceCopy = append(sliceCopy, 6)
sliceAddress := &mySlice
*sliceAddress = append(mySlice, 6) // or append(*sliceAddress, 6)