
php小编草莓为您介绍一种在golang中与汇编代码一起工作的方法——For-Range。For-Range是golang中的一个循环结构,可以与汇编代码结合使用,提供更高效的性能和灵活性。通过使用For-Range,您可以在golang中轻松处理大量的数据,并且可以借助汇编代码的优势,提升程序的执行效率。在本文中,我们将详细介绍For-Range的使用方法,并讲解如何与汇编代码进行协作,以实现更高效的程序运行。
当源代码被汇编时,我对 golang 中 for-range 内的指针用法感到困惑。例如,我们知道下面的变量value将始终位于相同的内存地址中,并且相应的汇编代码显示了相同的逻辑。
// Source Code
func main() {
a := []int{1, 3, 5}
for _, value := range a {
foo(&value)
}
}
func foo(a *int) int {
b := *a * 42
fmt.Println(b)
return b
}
// Assembly Code
"".main STEXT size=126 args=0x0 locals=0x38 funcid=0x0
0x0000 00000 (main.go:15) TEXT "".main(SB), ABIInternal, $56-0
0x0000 00000 (main.go:15) CMPQ SP, 16(R14)
0x0004 00004 (main.go:15) PCDATA $0, $-2
0x0004 00004 (main.go:15) JLS 119
0x0006 00006 (main.go:15) PCDATA $0, $-1
0x0006 00006 (main.go:15) SUBQ $56, SP
0x000a 00010 (main.go:15) MOVQ BP, 48(SP)
0x000f 00015 (main.go:15) LEAQ 48(SP), BP
0x0014 00020 (main.go:15) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0014 00020 (main.go:15) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0014 00020 (main.go:16) MOVQ $0, ""..autotmp_4+24(SP)
0x001d 00029 (main.go:16) LEAQ ""..autotmp_4+32(SP), CX
0x0022 00034 (main.go:16) MOVUPS X15, (CX)
0x0026 00038 (main.go:16) MOVQ $1, ""..autotmp_4+24(SP)
0x002f 00047 (main.go:16) MOVQ $3, ""..autotmp_4+32(SP)
0x0038 00056 (main.go:16) MOVQ $5, ""..autotmp_4+40(SP)
0x0041 00065 (main.go:16) XORL AX, AX
0x0043 00067 (main.go:18) JMP 103
0x0045 00069 (main.go:18) MOVQ AX, ""..autotmp_10+16(SP)
0x004a 00074 (main.go:18) MOVQ ""..autotmp_4+24(SP)(AX\*8), CX
0x004f 00079 (main.go:18) MOVQ CX, "".value+8(SP)
0x0054 00084 (main.go:19) LEAQ "".value+8(SP), AX // Here we see we always use "".value+8(SP) as the argument into foo()
0x0059 00089 (main.go:19) PCDATA $1, $0
0x0059 00089 (main.go:19) CALL "".foo(SB)
0x005e 00094 (main.go:18) MOVQ ""..autotmp_10+16(SP), CX
0x0063 00099 (main.go:18) LEAQ 1(CX), AX
0x0067 00103 (main.go:18) CMPQ AX, $3
0x006b 00107 (main.go:18) JLT 69
0x006d 00109 (main.go:21) PCDATA $1, $-1
0x006d 00109 (main.go:21) MOVQ 48(SP), BP
0x0072 00114 (main.go:21) ADDQ $56, SP
0x0076 00118 (main.go:21) RET
0x0077 00119 (main.go:21) NOP
0x0077 00119 (main.go:15) PCDATA $1, $-1
0x0077 00119 (main.go:15) PCDATA $0, $-2
0x0077 00119 (main.go:15) CALL runtime.morestack_noctxt(SB)
0x007c 00124 (main.go:15) PCDATA $0, $-1
0x007c 00124 (main.go:15) JMP 0
0x0000 49 3b 66 10 76 71 48 83 ec 38 48 89 6c 24 30 48 I;f.vqH..8H.l$0H
0x0010 8d 6c 24 30 48 c7 44 24 18 00 00 00 00 48 8d 4c .l$0H.D$.....H.L
0x0020 24 20 44 0f 11 39 48 c7 44 24 18 01 00 00 00 48 $ D..9H.D$.....H
0x0030 c7 44 24 20 03 00 00 00 48 c7 44 24 28 05 00 00 .D$ ....H.D$(...
0x0040 00 31 c0 eb 22 48 89 44 24 10 48 8b 4c c4 18 48 .1.."H.D$.H.L..H
0x0050 89 4c 24 08 48 8d 44 24 08 e8 00 00 00 00 48 8b .L$.H.D$......H.
0x0060 4c 24 10 48 8d 41 01 48 83 f8 03 7c d8 48 8b 6c L$.H.A.H...|.H.l
0x0070 24 30 48 83 c4 38 c3 e8 00 00 00 00 eb 82 $0H..8........
rel 90+4 t=7 "".foo+0
rel 120+4 t=7 runtime.morestack_noctxt+0但是,当我更改源代码并尝试查看汇编代码中的变化时,我发现没有任何变化。
// Changed Source Code
func main() {
a := []int{1, 3, 5}
for _, value := range a {
v := value
foo(&v)
}
}
func foo(a *int) int {
b := *a * 42
fmt.Println(b)
return b
}
// Changed Assembly Code
"".main STEXT size=126 args=0x0 locals=0x38 funcid=0x0
0x0000 00000 (main.go:15) TEXT "".main(SB), ABIInternal, $56-0
0x0000 00000 (main.go:15) CMPQ SP, 16(R14)
0x0004 00004 (main.go:15) PCDATA $0, $-2
0x0004 00004 (main.go:15) JLS 119
0x0006 00006 (main.go:15) PCDATA $0, $-1
0x0006 00006 (main.go:15) SUBQ $56, SP
0x000a 00010 (main.go:15) MOVQ BP, 48(SP)
0x000f 00015 (main.go:15) LEAQ 48(SP), BP
0x0014 00020 (main.go:15) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0014 00020 (main.go:15) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0014 00020 (main.go:16) MOVQ $0, ""..autotmp_5+24(SP)
0x001d 00029 (main.go:16) LEAQ ""..autotmp_5+32(SP), CX
0x0022 00034 (main.go:16) MOVUPS X15, (CX)
0x0026 00038 (main.go:16) MOVQ $1, ""..autotmp_5+24(SP)
0x002f 00047 (main.go:16) MOVQ $3, ""..autotmp_5+32(SP)
0x0038 00056 (main.go:16) MOVQ $5, ""..autotmp_5+40(SP)
0x0041 00065 (main.go:16) XORL AX, AX
0x0043 00067 (main.go:18) JMP 103
0x0045 00069 (main.go:18) MOVQ AX, ""..autotmp_11+16(SP)
0x004a 00074 (main.go:18) MOVQ ""..autotmp_5+24(SP)(AX\*8), CX
0x004f 00079 (main.go:19) MOVQ CX, "".v+8(SP)
0x0054 00084 (main.go:20) LEAQ "".v+8(SP), AX // Here we see the logic of argument is the same as above. This makes me confused.
0x0059 00089 (main.go:20) PCDATA $1, $0
0x0059 00089 (main.go:20) CALL "".foo(SB)
0x005e 00094 (main.go:18) MOVQ ""..autotmp_11+16(SP), CX
0x0063 00099 (main.go:18) LEAQ 1(CX), AX
0x0067 00103 (main.go:18) CMPQ AX, $3
0x006b 00107 (main.go:18) JLT 69
0x006d 00109 (main.go:22) PCDATA $1, $-1
0x006d 00109 (main.go:22) MOVQ 48(SP), BP
0x0072 00114 (main.go:22) ADDQ $56, SP
0x0076 00118 (main.go:22) RET
0x0077 00119 (main.go:22) NOP
0x0077 00119 (main.go:15) PCDATA $1, $-1
0x0077 00119 (main.go:15) PCDATA $0, $-2
0x0077 00119 (main.go:15) CALL runtime.morestack_noctxt(SB)
0x007c 00124 (main.go:15) PCDATA $0, $-1
0x007c 00124 (main.go:15) JMP 0
0x0000 49 3b 66 10 76 71 48 83 ec 38 48 89 6c 24 30 48 I;f.vqH..8H.l$0H
0x0010 8d 6c 24 30 48 c7 44 24 18 00 00 00 00 48 8d 4c .l$0H.D$.....H.L
0x0020 24 20 44 0f 11 39 48 c7 44 24 18 01 00 00 00 48 $ D..9H.D$.....H
0x0030 c7 44 24 20 03 00 00 00 48 c7 44 24 28 05 00 00 .D$ ....H.D$(...
0x0040 00 31 c0 eb 22 48 89 44 24 10 48 8b 4c c4 18 48 .1.."H.D$.H.L..H
0x0050 89 4c 24 08 48 8d 44 24 08 e8 00 00 00 00 48 8b .L$.H.D$......H.
0x0060 4c 24 10 48 8d 41 01 48 83 f8 03 7c d8 48 8b 6c L$.H.A.H...|.H.l
0x0070 24 30 48 83 c4 38 c3 e8 00 00 00 00 eb 82 $0H..8........
rel 90+4 t=7 "".foo+0
rel 120+4 t=7 runtime.morestack_noctxt+0那么局部变量 v 如何影响 for-range 呢?
立即学习“go语言免费学习笔记(深入)”;
正如上面的细节,我认为汇编代码应该显示新的局部变量的引入是如何工作的,但事实并非如此。
几点。
规范仅针对您的情况说明了以下内容:
这就是全部:它说将会有变量,并且它们将会 重新使用。后一点意味着,比如说,如果您创建一个闭包(使用函数文字的匿名函数),它将关闭一个或多个迭代变量,并将其返回/保存在某处并在循环结束后调用或者与循环同时(例如,在一个单独的 goroutine 中),该闭包将在循环的每次迭代更新(或正在更新)时访问这些完全相同的变量。
如果您不做任何此类奇特的事情 - 例如,仅从循环体代码中的那些变量中读取,那么这些变量被重用的事实是无关紧要的。
让我们重申一下:规范没有对迭代变量的内存地址提供任何保证。
为什么这很重要?因为编译器可以自由地生成它希望的任何代码,只要结果以遵循规范的方式工作,并且编译器的作用至少取决于以下内容:
目标硬件(GOARCH)。
Go 的版本和 make(实现)。不要低估这一点:例如,规范并没有精确定义 GC 的工作方式,因此任何品牌和任何版本的 Go 都可以自由地实现移动 GC,这将移动内存中的任意变量并更新指向的所有指针他们。
目前,流行的 Go 版本(您应该使用的版本和 GCC 前端)并没有这样做,但没有什么可以阻止它们或任何其他实现这样做。
因此,最后,您会问为什么特定的编译器会生成看起来特别的代码,并且既不说明 Go 的品牌,也不说明其版本,也不说明您的 GOARCH (尽管可以猜测它可能是 amd64) 。因此,您的问题实际上是无法回答的,并且精确的答案不会太有用,因为它们很快就会过时。
因此,此类问题与 SO 无关。
我决定将所有这些作为答案,只是因为对于评论来说太多了。
在您的特定情况下,编译器可能分析了 foo 的代码,并发现它不会更新通过指针参数传递给它的变量,并且更重要的是不会将其进一步传递到调用堆栈。由于循环体中的 v 没有做任何其他事情,编译器可能会认为语义 v 可以被视为 variable 的纯粹别名,并且它只是跳过了该单独变量的创建。我可以想象,如果您将这些变量的指针传递给 foo,并且让它打印这些地址,编译器将被迫实现 v。
请注意,“关于”只是一个猜测。如果您想了解所有细节,您可以随时研究编译器的工作原理(Go 的两种流行实现都是 F/OSS 的一部分)和/或检测其代码。
另请注意,您可以要求编译器告诉您有关其功能的更多信息。最常用的 Go 实现(最初称为 gc )在其 go build 和 go install 调用中支持 -gcflags 命令行参数,该调用将参数传递给编译器(请参阅 go 工具编译 -help )。特别是,它的 -m 和 -N (还有 -S 和 -live)标志可能值得玩一下。
以上就是For-Range 如何在 golang 中与汇编代码一起工作?的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号