请教一个 Golang Go语言中的函数问题

发布于 1周前 作者 yuanlaile 来自 Go语言

请教一个 Golang Go语言中的函数问题
代码片段如下
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 的结果完全是个随机数


更多关于请教一个 Golang Go语言中的函数问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html

29 回复

不懂 C++,想问一下 C++有没有闭包的概念?

另外 Go 会自动初始化空值,比如 var ss St 之后,ss.Val 会自动初始化为 0,这里 C++ 是类似的规定吗?

更多关于请教一个 Golang Go语言中的函数问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


f 是个 function 啊

c++ 没有闭包。ss.Val 是 0 我能理解,但是不太理解为什么 ss.Val 是 100

贴代码可以用 markdown 语法,```
或者 gist

这样的代码太难看了

另外,可以把 C++ 是随机数的代码也贴出来对比一下

ss 的地址指到了 s

给你换种写法,就是个赋值而已

<br>type St struct {<br> Val int<br>}<br><br>func f1(s *St, v int) {<br> s.Val = v<br>}<br><br>func TestA(t *testing.T) {<br> var ss St<br> f1(&amp;ss, 100)<br><br> fmt.Println(ss.Val)<br>}<br>

就通过传值设置进去的啊,你的疑问点是在于闭包?

因为闭包保存的是你闭包环境,你的闭包环境下 i 是 100,所以你执行它的时候设置的就是 100

嗯,你这样写我能理解。其实我想问的是 100 这个值存到哪里去了。它为什么能正确设置

https://tour.golang.org/moretypes/25

经典闭包
<br>package main<br><br>import "fmt"<br><br>func adder() func(int) int {<br> sum := 0<br> return func(x int) int {<br> sum += x<br> return sum<br> }<br>}<br><br>func main() {<br> pos, neg := adder(), adder()<br> for i := 0; i &lt; 10; i++ {<br> fmt.Println(<br> pos(i),<br> neg(-2*i),<br> )<br> }<br>}<br><br>

非常感谢,闭包这个东西以前没碰到过,正在理解中。。。。

c++ capture list ???

C++你把引用捕获改为赋值捕获就是 100 了

真是大神,佩服

Set(100) --> i = 100 --> f(&ss) --> s = &ss --> ss.Val = s.Val = i = 100

go 中关于闭包的一些实现过程可以看这个, [go 闭包的实现]( https://tiancaiamao.gitbooks.io/go-internals/content/zh/03.6.html)

同时,把你的代码复制到,https://godbolt.org/ , 选择 go 语言,再选择 386gc tip,对照汇编结果来理解。

正解
楼主 c++ lambda 引用了一个临时变量

谢谢,应该可以理解的更快一点了

c++要把对象的作用域 生命周期在脑子里想的很清楚
我感觉这是最难地方 比语法规则难多了

好吧,c++ capture 换成 = 号后我又有一个疑问,这个值是什么时候复制的,是在调 Set 的时候还是调 f 的时候???

闭包,Set 执行完后返回的是一个函数,再执行把指针传进去,那肯定把 ss 的值给改了

两步都有复制。调用 Set 的时候,复制了 100 到一个地方存下来。调用 f 的时候,再把存下来的值复制给 s._i

在 lambda 表达式的内外,参数 i 的意义是不一样的。在表达式的内部:1. 引用捕获的时候,它是参数 i 的引用,调用 f 的时候,参数 i 已经没有了,所以最终得到的结果是随机的; 2. 赋值捕获的时候,它是参数 i 的复制

可能还是代码更直观点一点,Set 可以写成下面这样
cpp<br>std::function&lt;void(St*)&gt; Set(int i) {<br> // 赋值捕获<br> return [j=i](St* s) {<br> s-&gt;Set(j);<br> };<br>// // 引用捕获<br>// return [&amp;j=i](St* s) {<br>// s-&gt;Set(j);<br>// };<br>}<br>

嗯,我有点明白了,执行 Set 的时候把 i 复制到了 j,执行 f 的时候函数引用的就是捕获的值 j,也就是 i 的一个复制

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)

当然,很高兴帮助你解答关于Golang函数的问题。

在Go语言中,函数是代码的基本构建块,用于执行特定的任务。函数定义包括函数名、参数列表(可选)、返回类型(如果函数有返回值的话)以及函数体。

以下是一个简单的函数定义示例:

package main

import "fmt"

// 定义一个名为 add 的函数,接收两个整数参数并返回一个整数
func add(a int, b int) int {
    return a + b
}

func main() {
    // 调用 add 函数并打印结果
    result := add(3, 4)
    fmt.Println(result) // 输出: 7
}

在Go中,函数可以有多个返回值。例如:

func swap(x, y string) (string, string) {
    return y, x
}

在调用函数时,可以使用命名返回值来提前设置返回值,并在函数体中修改它们:

func modifyValues() (a, b int) {
    a = 1
    b = 2
    a, b = b, a + b
    return
}

此外,Go还支持匿名函数和闭包,它们可以在需要的地方定义并使用,非常适合在回调函数和迭代器等场景中使用。

如果你有具体的函数问题或代码示例,请提供详细信息,这样我可以给出更具体的帮助和建议。

回到顶部