切片或数组是否充当全局范围?

Do slices or arrays act as a global scope?

我对编程还是个新手。请原谅我缺乏计算机科学知识。不确定这个问题是特定于 Golang 还是一般的计算机科学...

我一直认为函数不会改变 variables/data 保持在它们自己的范围之外,除非你使用 return 语句回到另一个范围,或者除非它们在范围层次结构中更高。有人可能会争辩说,此示例中的函数 f1f2 是从较低范围调用的。但是,这仍然不能解释为什么我对变量 numnums.

得到不同的结果
package main

import "fmt"

func f1(a int) {
    a = 50 // this will not work, as it shouldn't
}

func f2(a ...int) {
    a[0] = 50 // this will work without return statement
    a[1] = 50 // this will work without return statement
}

func main() {

    num := 2
    nums := []int{2, 2}

    f1(num)
    f2(nums...)

    fmt.Printf("function f1 doesn't affect the variable num and stays: %v\n", num)
    fmt.Printf("function f2 affects the variable nums and results in: %v", nums)

问题:

  1. 为什么 f2 不需要像 num 这样的 return 语句来修改 nums 会在 f1 内吗?
  2. 据说 Golang 函数传递值(而不是引用), 这不应该强制函数 return 份吗?[=​​29=]
  3. 这会发生在其他语言中吗? (我想我可能有 在其他语言中看到过这个)。

在go中,函数参数是按值传递的。这意味着,如果您传递一个 int(如 f1),编译器将传递 f1 的值,本质上是复制它。如果函数采用 *int 而你传递 &num,则编译器传递 &num 的值,它是指向 num 的指针。当函数改变*num时,函数外变量的值也会改变。如果函数改变numnum的指针值也会改变,指向不同的变量

相比之下,Java 将所有原始值作为值传递,而所有 objects 通过引用传递。也就是说,如果您传递一个 int,函数将无法修改调用者可见的那个 int 的值。如果你想传递一个 int 函数可以修改,你把它放在 class 中,然后在 Java.

中传递那个 class 的实例

一个切片(如 f2)包含一个指向底层数组的指针。当您使用切片调用函数时,切片 header(包含指向底层数组的指针)被复制,因此当函数更改切片元素时,底层数组元素也会更改。

范围问题有些不同。函数的范围是它可以看到的所有变量。这些是全局变量(如果来自不同的包,导出的全局变量),函数参数,如果函数被声明为嵌套在另一个函数中,则所有变量在该函数中可见。

这是正确的行为,因为 a ...int 等于一个切片,例如:a []int

func f2(a []int) {
    a[0] = 50
    a[1] = 50
}
func main() {
    b := []int{2, 2}
    f2(b)
    fmt.Println(b) // [50 50]
}

切片是对原始数据的视图,这里'b'。

"Why doesn't f2 require a return statement to modify nums like num would in f1?"

f2 中,您使用的是切片,它有一个指向原始数组的指针,因此 f2 可以更改外部数组。

"Golang functions are said to pass values (not reference), shouldn't that force to return copies? (If the question is related...)"

f2中切片本身是按值传递的,意思是指针和原始数组的长度和容量。

"Can this happen in other languages? (I think I may have seen this in other langues)"

范围太广,无法回答,有很多种语言,一般来说,如果您有一个指向外部世界数组的指针,是的。


编辑:

package main

import "fmt"

func sum(a ...int) int {
    s := 0
    for _, v := range a {
        s += v
    }
    return s
}
func f2(a []int) {
    c := make([]int, len(a))
    copy(c, a)
    c[0] = 50
    fmt.Println(sum(c...)) // 52
}
func main() {
    b := []int{2, 2}
    fmt.Println(sum(1, 2, 3, 4)) // 10
    fmt.Println(sum(b...))       // 4

    f2(b)
    fmt.Println(b) // [2 2]
}

备注:
上面的 sum() 函数是一个纯函数,因为它没有副作用。
上面的新 f2 函数是一个纯函数,因为它没有副作用:它将 a 复制到 c 然后调用求和。

1 & 2) 在查看切片在 Go 中的工作方式时,这两个问题都可以得到回答。上面有a blog article

一般情况下,Go中的所有变量都是按值传递的。您可以使用指针(例如 *int 表示 f1)通过引用(或更正确的指针地址)传递。 但是,切片在技术上也是按值传递的。

当我们查看 here 时,我们可以了解它们的工作原理:

type SliceHeader struct {
    Data uintptr
    Len  int
    Cap  int
}

LenCap是整数,但是Data是指针。

复制此结构时(按值传递时),将制作 LenCapData 的副本。由于 Data 是一个指针,因此对它指向的值所做的任何修改都将在您的函数 returns.

之后可见

你也可以