Tel*_*hus 5 memory-management go
(我正在使用 Donovan 和 Kernighan 的The Go Programming Language学习 Go 。这个问题的答案对其他人来说可能是显而易见的,但我很难过,不知道从哪里开始。)
作为GOPL 中的一个练习,作者要求读者修改他们的reverse程序(将ints的切片原地反转)“以原地反转[]byte表示 UTF-8 编码字符串的切片的字符”(93)。他们补充说:“你能在不分配新内存的情况下做到这一点吗?”
简而言之,我想问以下是否分配了新内存。根据打印语句的结果,我认为它没有,但我不确定。另一种让我感到困惑的reverse方法是:如果该方法就地反转,我希望它不会分配新的内存。所以,我假设我一定遗漏了一些东西,因为他们要求方法就位,然后增加不分配新内存的挑战。他们是在催促我避免我做过的事情吗?这里是否有一些额外的技巧值得了解内存分配(在 Go 中或一般情况下)?
package main
import (
"fmt"
"unicode/utf8"
)
func main() {
phrase := "Hello, ??!"
fmt.Printf("Before reverse:\tmemory address %p => phrase: %s\n", &phrase, phrase)
phrase = string(reverseByRune([]byte(phrase)))
fmt.Printf("After reverse:\tmemory address %p => phrase: %s\n", &phrase, phrase)
}
func reverseByRune(b []byte) []byte {
for i := 0; i < len(b); {
_, size := utf8.DecodeRune(b[i:])
reverse(b[i : i+size])
i += size
}
reverse(b)
return b
}
func reverse(b []byte) []byte {
for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 {
b[i], b[j] = b[j], b[i]
}
return b
}
Run Code Online (Sandbox Code Playgroud)
这是 Go Playground:https : //play.golang.org/p/Qn7nYXLGoQn。
PS 我只能投赞成票并接受一次,但如果有人想要额外的感谢,我会喜欢任何有关内存分配的链接(尤其是在 Go 中)。
编辑:问题中的实现可能也不会在堆上分配内存,但建议的修改仍然应该更有效一点。我最初认为问题中的代码来自书中,并且由于缺乏本地编译器而无法检查内存分配。
\n这是一个我认为应该没有内存分配的实现。
\nhttps://play.golang.org/p/N4mkYoiIHvn
\npackage main\n\nimport (\n "fmt"\n "unicode/utf8"\n)\n\nfunc main() {\n phrase := "Hello, \xe4\xb8\x96\xe7\x95\x8c!"\n fmt.Printf("Before reverse:\\tmemory address %p => phrase: %s\\n", &phrase, phrase)\n phrase = string(reverseByRune([]byte(phrase)))\n fmt.Printf("After reverse:\\tmemory address %p => phrase: %s\\n", &phrase, phrase)\n}\n\nfunc reverseByRune(b []byte) []byte {\n reverse := func (i, j int) {\n for ; i < j; i, j = i+1, j-1 {\n b[i], b[j] = b[j], b[i]\n }\n }\n for i := 0; i < len(b); {\n _, size := utf8.DecodeRune(b[i:])\n reverse(i, i+size-1)\n i += size\n }\n reverse(0, len(b)-1)\n return b\n}\nRun Code Online (Sandbox Code Playgroud)\n的原始实现reverse()需要内存来创建b []byte切片并且也不必要地返回一个值。尽管理论上可以通过return从 中删除来优化reverse。然后编译器可以“猜测”没有人可以保留指向切片的指针,并且可以确保切片是在堆栈而不是堆上创建的。但这只是理论上的推测——我不确定 Go 的编译器是否那么聪明。
建议的实现与原始实现相同,但在原始切片内。
\n当我们谈论“无内存分配”时,我们通常指的是函数内发生的情况,在本例中为reverseByRune(). i像&这样分配在堆栈上的局部变量j 不算在内,因为它们很便宜。
对于给定方法,这可能是最有效的实现:
\nhttps://play.golang.org/p/YOOSZjIWKZ_r
\npackage main\n\nimport (\n "fmt"\n "unicode/utf8"\n)\n\nfunc main() {\n phrase := []byte("Hello, \xe4\xb8\x96\xe7\x95\x8c!")\n fmt.Printf("Before reverse:\\tmemory address %p => phrase: %s\\n", &phrase, string(phrase))\n reverseByRune(phrase)\n fmt.Printf("After reverse:\\tmemory address %p => phrase: %s\\n", &phrase, string(phrase))\n}\n\nfunc reverseByRune(b []byte) {\n for i := 0; i < len(b); {\n _, size := utf8.DecodeRune(b[i:])\n for k, p := i, i+size-1; k < p; k, p = k+1, p-1 {\n b[k], b[p] = b[p], b[k]\n }\n i += size\n }\n for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 {\n b[i], b[j] = b[j], b[i]\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n但这太过分了!
\n| 归档时间: |
|
| 查看次数: |
135 次 |
| 最近记录: |