RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 1570354
Accepted
Space Researcher
Space Researcher
Asked:2024-03-06 16:18:35 +0000 UTC2024-03-06 16:18:35 +0000 UTC 2024-03-06 16:18:35 +0000 UTC

追加和切片可变性

  • 772
package main

import (
    "fmt"
    "log"
)

func main() {
    arr := make([]int, 3, 3)
    fmt.Println(arr, len(arr), cap(arr))
    log.Printf("%p", arr)
    addElems(arr)
    fmt.Println(arr, len(arr), cap(arr))
    log.Printf("%p", arr)
}
func addElems(arr []int) {
    log.Printf("%p", arr)
    arr = append(arr, 4)
    arr = append(arr, 5)
    arr = append(arr, 6)
    fmt.Println(arr, len(arr), cap(arr))
    log.Printf("%p", arr)
}

切片在函数内部发生变化,这就是 Go 的工作方式,但如果你执行追加操作,那么一切都会不同。我认为这很可能是因为append返回新地址并将其插入到arr变量中,但不清楚为什么arr本身没有变异。

我决定再举一个例子

package main

import (
    "fmt"
    "log"
)

func main() {
    arr := make([]int, 3, 8)
    fmt.Println(arr, len(arr), cap(arr))
    log.Printf("%p", arr)
    addElems(arr)
    fmt.Println(arr, len(arr), cap(arr))
    log.Printf("%p", arr)
}
func addElems(arr []int) {
    log.Printf("%p", arr)
    arr = append(arr, 4)
    arr = append(arr, 5)
    arr = append(arr, 6)
    fmt.Println(arr, len(arr), cap(arr))
    log.Printf("%p", arr)
}

这里append没有返回新的地址,为什么函数出口处有一个arr,但在main函数中运行后它已经不同了?

golang
  • 3 3 个回答
  • 66 Views

3 个回答

  • Voted
  1. Best Answer
    Pak Uula
    2024-03-06T18:54:23Z2024-03-06T18:54:23Z

    切片不是指针。这是一个包含三个字段的数据结构,通过 value传递给函数。

    在这里,看:

    package main
    
    import (
        "fmt"
    )
    
    func main() {
        arr := make([]int, 3, 3)
        fmt.Println("main: ", arr, len(arr), cap(arr))
        fmt.Printf("main: %p->%p\n", &arr, arr)
        addElems(arr)
        fmt.Println("main: ", arr, len(arr), cap(arr))
        fmt.Printf("main: %p->%p\n", &arr, arr)
    }
    func addElems(arr []int) {
        fmt.Printf("  addElems: %p->%p\n", &arr, arr)
        arr = append(arr, 4)
        arr = append(arr, 5)
        arr = append(arr, 6)
        fmt.Println("  ", arr, len(arr), cap(arr))
        fmt.Printf("  addElems: %p->%p\n", &arr, arr)
    }
    
    main:  [0 0 0] 3 3
    main: 0xc000010018->0xc00001a018
      addElems: 0xc000010060->0xc00001a018
       [0 0 0 4 5 6] 6 6
      addElems: 0xc000010060->0xc000108000
    main:  [0 0 0] 3 3
    main: 0xc000010018->0xc00001a018
    

    该main变量arr位于该地址0xc000010018,指向数据的指针是0xc00001a018

    但在函数中,addElems所有操作都发生在完全不同的对象上,该对象位于内存中的地址处0xc000010060。那。该对象中的指针已更改不会以任何方式反映在第一个对象上。与lenand类似cap,它们更改了堆栈上的值addElems,但这些更改不会以任何方式影响原始对象。

    在这种情况下,make([]int, 3, 8)指针没有改变,因为不需要重新分配。但即使在这种情况下,原始切片也不知道副本中的变化,因此尽管添加了三个元素,但也没有发生变化len。cap

    如果需要更新原始对象,则需要传递一个指针

    func addElems(arr *[]int) {
        fmt.Printf("  addElems: %p->%p\n", arr, *arr)
        *arr = append(*arr, 4)
        *arr = append(*arr, 5)
        *arr = append(*arr, 6)
        fmt.Println("  ", *arr, len(*arr), cap(*arr))
        fmt.Printf("  addElems: %p->%p\n", arr, *arr)
    }
    

    或返回修改后的对象

    func addElems(arr []int) []int {
        fmt.Printf("  addElems: %p->%p\n", &arr, arr)
        arr = append(arr, 4)
        arr = append(arr, 5)
        arr = append(arr, 6)
        fmt.Println("  ", arr, len(arr), cap(arr))
        fmt.Printf("  addElems: %p->%p\n", &arr, arr)
        return arr
    }
    

    样式指南推荐第二种方法,返回更新后的值。

    • 2
  2. Kreket Jot
    2024-03-06T19:12:59Z2024-03-06T19:12:59Z

    切片下的数组本身发生了变化,这可以通过写入afterarr看到,这将基于同一个数组创建一个长度等于数组容量的新切片。arr = arr[:cap(arr)]mainaddElems(arr)

    slice arrfromaddElems是从 from 的切片创建的切片main。

    您可以在此处阅读有关数组和切片的更多信息: https: //go.dev/blog/slices-intro

    您可以像这样缩短日志:fmt.Printf(“%p %v len=%v cap=%v\n”, arr, arr, len(arr), cap(arr))

    • 1
  3. Quester
    2024-03-06T17:46:46Z2024-03-06T17:46:46Z

    追加适用于副本,而不适用于底层数组

    如果需要追加返回新地址,请添加 3 个以上的值。

    append 使用 cap= cap(old slice)*2 创建一个新切片,虽然旧切片的 cap 对他来说已经足够了,但他却陷入了阑尾。

    事实证明,为了更改地址,您需要添加的不仅仅是 cap(arr)

    func main() {
        arr := make([]int, 3, 8)
        // fmt.Println(arr, len(arr), cap(arr))
        // log.Printf("%p", arr)
        addElems(arr)
        // fmt.Println(newArr, len(newArr), cap(newArr))
        // log.Printf("%p", newArr)
    }
    
       func addElems(arr []int)  {
        log.Printf("%p", arr)
        arr = append(arr, 4)
        arr = append(arr, 4)
        arr = append(arr, 4)
        log.Printf("%p", arr)
    
        arr = append(arr, 4)
        arr = append(arr, 4)
        arr = append(arr, 4)
        log.Printf("%p", arr)
    
        arr = append(arr, 4)
        arr = append(arr, 4)
        log.Printf("%p", arr)
        arr = append(arr, 4)
        arr = append(arr, 4)
        arr = append(arr, 4)
        log.Printf("%p", arr)
    
        arr = append(arr, 4)
        arr = append(arr, 4)
        arr = append(arr, 4)
        log.Printf("%p", arr)
    
        arr = append(arr, 4)
        arr = append(arr, 4)
        log.Printf("%p", arr)
    
        fmt.Println(arr, len(arr), cap(arr))
        log.Printf("%p", arr)
        
    }
    

    我的输出是这样的:

    2024/03/06 13:05:50 0xc000020200
    2024/03/06 13:05:50 0xc000020200
    2024/03/06 13:05:50 0xc00011a000
    2024/03/06 13:05:50 0xc00011a000
    2024/03/06 13:05:50 0xc00011a000
    2024/03/06 13:05:50 0xc00011c000
    2024/03/06 13:05:50 0xc00011c000
    [0 0 0 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4] 19 32
    2024/03/06 13:05:50 0xc00011c000
    

    每次扩展上限时(即每次复制到新的基本数组时),地址都会发生变化

    • -1

相关问题

  • windows上的protoc编译错误

  • 递归打印包依赖

  • Golang 算法 XTEA ECB 库“golang.org/x/crypto/xtea”

  • 如何将 IMEI 转换为字节并返回 golang

  • 如何创建文件并将其移动到新目录?

  • go中的函数参数中是否有cv-qualifier的类似物?

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    我看不懂措辞

    • 1 个回答
  • Marko Smith

    请求的模块“del”不提供名为“default”的导出

    • 3 个回答
  • Marko Smith

    "!+tab" 在 HTML 的 vs 代码中不起作用

    • 5 个回答
  • Marko Smith

    我正在尝试解决“猜词”的问题。Python

    • 2 个回答
  • Marko Smith

    可以使用哪些命令将当前指针移动到指定的提交而不更改工作目录中的文件?

    • 1 个回答
  • Marko Smith

    Python解析野莓

    • 1 个回答
  • Marko Smith

    问题:“警告:检查最新版本的 pip 时出错。”

    • 2 个回答
  • Marko Smith

    帮助编写一个用值填充变量的循环。解决这个问题

    • 2 个回答
  • Marko Smith

    尽管依赖数组为空,但在渲染上调用了 2 次 useEffect

    • 2 个回答
  • Marko Smith

    数据不通过 Telegram.WebApp.sendData 发送

    • 1 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5