请教一个 go 的函数问题

2020-10-22 15:48:19 +08:00
 loriann
代码片段如下
type St struct {
Val int
}
type f Func(s *St)
func Set(i int) f {
return func(s *St){
s.Val = i
}
}

func main(){
f := Set(100)
var ss St
f(&ss)

// ss.Val = 100 ????
}

最后我好奇的是,这个 100 是怎么设置进去的,同样的代码逻辑我用 c++ s.Val 的结果完全是个随机数
2744 次点击
所在节点    Go 编程语言
28 条回复
zunceng
2020-10-22 17:10:49 +08:00
c++要把对象的作用域 生命周期在脑子里想的很清楚
我感觉这是最难地方 比语法规则难多了
loriann
2020-10-22 17:14:38 +08:00
@catror 好吧,c++ capture 换成 = 号后我又有一个疑问,这个值是什么时候复制的,是在调 Set 的时候还是调 f 的时候???
linjunyi22
2020-10-22 17:37:11 +08:00
闭包,Set 执行完后返回的是一个函数,再执行把指针传进去,那肯定把 ss 的值给改了
catror
2020-10-22 18:04:00 +08:00
@loriann 两步都有复制。调用 Set 的时候,复制了 100 到一个地方存下来。调用 f 的时候,再把存下来的值复制给 s._i
catror
2020-10-22 18:09:44 +08:00
@loriann 在 lambda 表达式的内外,参数 i 的意义是不一样的。在表达式的内部:1. 引用捕获的时候,它是参数 i 的引用,调用 f 的时候,参数 i 已经没有了,所以最终得到的结果是随机的; 2. 赋值捕获的时候,它是参数 i 的复制
catror
2020-10-22 18:19:22 +08:00
可能还是代码更直观点一点,Set 可以写成下面这样
```cpp
std::function<void(St*)> Set(int i) {
// 赋值捕获
return [j=i](St* s) {
s->Set(j);
};
// // 引用捕获
// return [&j=i](St* s) {
// s->Set(j);
// };
}
```
loriann
2020-10-22 18:24:00 +08:00
@catror 嗯,我有点明白了,执行 Set 的时候把 i 复制到了 j,执行 f 的时候函数引用的就是捕获的值 j,也就是 i 的一个复制
katsusan
2020-10-22 18:32:29 +08:00
Go 里的闭包函数在内存里是 code+data 形式表现的。

以你 Set 函数返回的 f 为例,在编译期就可以分析出它应该包含内部匿名函数的入口点地址和 i 的值,
在 x64 上就是 16 字节空间,也就是 main 栈上的 f 指向一个 16 字节的空间,执行 f=Set(100)的赋值后
第二个 8 字节就赋值为 100 。

执行 f(&ss)时参照 https://golang.org/s/go11func,在 x86_64 下 R0 用的是 DX 寄存器。

MOV …, R0

MOV 0(R0), R1 //R0+0=>R1,即函数入口地址

CALL R1 # called code can access “data” using R0,比如例子里的 100 就是 8(R0)

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/717494

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX