V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
frank1256
V2EX  ›  Go 编程语言

go 我就不用指针不行?

  •  
  •   frank1256 · 136 天前 · 4547 次点击
    这是一个创建于 136 天前的主题,其中的信息可能已经有所发展或是发生改变。

    rt ,

    搜索引擎告诉我的都是,要改变属性的话就要传指针。是因为如果用值传递,会进行复制一份。这我能理解。

    如果不需要改属性就使用值传递,但是不也是复制了一份吗???只不过我没修改属性罢了。

    那综上改属性和不改属性都会复制。那什么时候应该用值传递?值传递的所谓的复制一份,危害性很大?那为什么 go 要设计出值传递?

    还有人说无脑用指针就行?平时的 crud 里,除了查询出结果的时候,做的赋值动作。需要指针,其他函数之间的流转基本都不会去修改属性的。我用值传递不行吗。

    求大佬解答

    28 条回复    2022-05-28 14:00:29 +08:00
    cmdOptionKana
        1
    cmdOptionKana  
       136 天前   ❤️ 9
    你以前用什么语言,没有值传递、引用传递的区别吗?
    mainjzb
        2
    mainjzb  
       136 天前
    指针很小,复制的代价很小很小。
    值复制不依赖 gc ,可以减轻 gc 压力。只读的情况下通常复制值给程序带来的压力更小。所以通常建议读取的时候用值复制。
    你说的很乱,没太看懂
    CokeMine
        3
    CokeMine  
       136 天前 via Android
    深拷贝损失性能
    你可以去写 JavaScript (
    frank1256
        4
    frank1256  
    OP
       136 天前
    @mainjzb 了解了,描述不太清楚
    pultako
        5
    pultako  
       136 天前   ❤️ 2
    go 里面参数传递只有值拷贝这一种方式, 你说的"值传递"和"传指针"两者的区别仅仅在于是拷贝的是一整个结构体还是指针
    无脑用指针也是不可取的, 因为内存逃逸的原因, GC 压力会很大.
    一般来说在 crud 里, 查询结果返回使用指针传递, 请求的参数结构体 /中间变量直接使用结构体.
    YUyu101
        6
    YUyu101  
       136 天前
    不是大块数据结构,内存复制快啊,以前写 java 好像只有 integer 这些基础类型会拆箱成值传递,其他都是对象分配在堆上传引用,go 自己怎么传自由一点。
    chengyiqun
        7
    chengyiqun  
       136 天前
    值传递又不是 go 独有的, 很多语言都有值传递, 而且占据大多数, 像 c 和 java.
    而且 java 不存在引用传递一说, 全都是值传递. 只是传递对象的时候, 传递的是对象的指针, 所以仿佛看上去像引用传递而已.
    值传递的目的应该是为了减少变量的作用范围, 因为要用到引用传递的地方通常是没有值传递多的.
    很多时候, 一个函数输入数据, 输出数据, 不需要改变原输入数据.
    chengyiqun
        8
    chengyiqun  
       136 天前
    而且正如楼上说的, 值传递复制的变量是在栈上的, 出了方法就自动销毁, 回收代价很小, 分配很快.
    Mexion
        9
    Mexion  
       136 天前
    不改属性确实直接可以用值,但是如果结构数据很大,copy 性能较低
    txx
        10
    txx  
       136 天前
    之前一直以为 Go 的 值传递也和 Swift 一样是 Copy-on-Write ... 😂
    littlewing
        11
    littlewing  
       136 天前
    不改变属性应该用

    const T&

    或者 非 mut borrowing

    手动狗头.jpg
    lxz6597863
        12
    lxz6597863  
       136 天前
    粗暴点来讲:
    对象生命周期很长的,用指针来传
    对象结构数据很大的,用指针来传

    其他不改动属性的情况,无脑用值
    fgwmlhdkkkw
        13
    fgwmlhdkkkw  
       136 天前
    所有语言传参都是传值!!!!!!但值的意义由你来定。
    lysS
        14
    lysS  
       136 天前
    go 的指针是安全的,不想 C 的
    wenning
        15
    wenning  
       136 天前
    @lxz6597863 赞同你的意见; 大多数时候无脑用值, 还能避免很多空指针的 panic
    yousabuk
        16
    yousabuk  
       136 天前 via iPhone
    非超级大数据量值传递没有危害,甚至都不会影响电脑性能。

    内存拷贝的压力很小的,试了下,采集 4 台射频信号,100M IQ 采样速率(每台每秒 100 * 2 * 2 = 400Mbyte )改用内存拷贝的方式再进行数据运算处理抽值绘制波形图,也够的,不影响电脑使用。当然,还是会选择用指针的方式来操作数据。
    XTTX
        17
    XTTX  
       136 天前   ❤️ 1
    @fgwmlhdkkkw #13 啊?!!!!!! 你是怎么敢打那么多感叹号的
    ligiggy
        18
    ligiggy  
       136 天前
    @XTTX !!!!!!!!!!!!!!!!!
    GeruzoniAnsasu
        19
    GeruzoniAnsasu  
       136 天前   ❤️ 1
    如下机制必须使用指针:

    type T struct {
    Field int
    }

    func (t *T) SetValue(v int) {
    t.Field = v
    }

    var t T
    t.SetValue(1) // 否则 t 的值永远不会改变

    如下结构传递值会有问题:
    defer func(v T){someChan <-v}(vv) // channel 捕捉不到 v 的最后状态而是当前值

    gorm 的 scan 使用了大一 C 语言教科书级别的指针传递:
    db.Where("id = ?", 1).Scan(&ModeledValue)




    无脑用指针的话:

    func (*T) TableName() string {
    return "t_"
    }

    你要写 db.Table((&T{}).TableName()) ,本来可以 db.Table(T{}.TableName())




    还有浅拷贝的问题:

    type T struct{v:int}
    type U struct{t *T}
    type N struct {u *U}

    t := T{v:1}
    u := U{}
    n := N{}

    n.u = &u
    u.t = &t


    func foo(n *N) N {
    return N{u: &U{t: n.u.t}}// 我想「用 n 的值初始化一个新 N 」
    }

    newN := foo(&n)
    newN.u.t.v = 2
    n.u.t.v == 2 //但其实 n.u.t 被改了


    ref: https://go.dev/play/p/74Q6bC13xN0
    BeautifulSoap
        20
    BeautifulSoap  
       136 天前   ❤️ 2
    排除需要修改值得情况,大部分人纠结传指针还是传值往往是在纠结对 struct 来说,我到底是该传值还是指针

    很多人都会有种误解,认为传 struct 的指针比复制一份值快很多,所以喜欢传 struct 的指针。但实际上并不是的,指针引用的对象是分配到堆上的,在函数内使用指针引用的值都需要取堆去取,并且堆中的内存受 GC 管理会增加 GC 压力。而传值的话复制后的值会直接分配在栈上,栈的速度比堆快,并且函数执行完毕后栈会销毁没有 GC 之类的压力。

    所以传值还是传指针,还是要取决于 struct 的大小,如果 struct 本身很大,复制一个 struct 的成本大于用指针直接引用的性能消耗那么可以考虑传指针。
    duckyrain
        21
    duckyrain  
       136 天前
    优先使用值传递。如果指针传递更优,go 就不会默认用值传递了。两篇文章参考:

    值传递 vs 指针传递 https://goinbigdata.com/golang-pass-by-pointer-vs-pass-by-value/
    值返回 vs 指针返回 https://philpearl.github.io/post/bad_go_pointer_returns/
    xfriday
        22
    xfriday  
       136 天前
    因为 go 没有 rust 的不可变借用
    nightwitch
        23
    nightwitch  
       136 天前
    语言设计成值语义还是对象语义只是一种品味问题。
    可以看看陈硕的文章: https://www.cnblogs.com/solstice/archive/2011/08/16/2141515.html

    在默认对象语义的语言里,比如 Python 这种,想要复制一份值要显式的调用 copy.deepcopy(),否则可能在函数里意外地修改函数外变量的值,很容易创造隐藏的 bug 。
    tomari
        24
    tomari  
       136 天前
    大一第一学期 C++课就说了啥时候传参用指针和引用,一是改变值,二是数据太大复制浪费时间和空间,依稀记得这个大一的期末考也会考。
    cassyfar
        25
    cassyfar  
       135 天前
    这是 stackoverflow 大学毕业的吗?
    Kasumi20
        26
    Kasumi20  
       135 天前
    什么叫 go 设计出值传递, 所有的数据都是值, 包括指针也是 4 字节或者 8 字节的值, OK?
    mengzhuo
        27
    mengzhuo  
       135 天前
    可以,就是对其他程序员不好理解。
    性能没差多少,倒是很好通过 channel 扩展。
    349865361
        28
    349865361  
       72 天前
    我都是传递结构体的时候用引用,单变量不需要修改的直接传, 切片和 map 本身就有引用树形所以也直传
    关于   ·   帮助文档   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   1152 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 20:17 · PVG 04:17 · LAX 13:17 · JFK 16:17
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.