这个 Go 方法是否“分配新内存”?

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 中)。

Ale*_*nok 1

编辑:问题中的实现可能也不会在堆上分配内存,但建议的修改仍然应该更有效一点。我最初认为问题中的代码来自书中,并且由于缺乏本地编译器而无法检查内存分配。

\n

这是一个我认为应该没有内存分配的实现。

\n

https://play.golang.org/p/N4mkYoiIHvn

\n
package 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}\n
Run Code Online (Sandbox Code Playgroud)\n

的原始实现reverse()需要内存来创建b []byte切片并且也不必要地返回一个值。尽管理论上可以通过return从 中删除来优化reverse。然后编译器可以“猜测”没有人可以保留指向切片的指针,并且可以确保切片是在堆栈而不是堆上创建的。但这只是理论上的推测——我不确定 Go 的编译器是否那么聪明。

\n

建议的实现与原始实现相同,但在原始切片内。

\n

当我们谈论“无内存分配”时,我们通常指的是函数内发生的情况,在本例中为reverseByRune(). i像&这样分配在堆栈上的局部变量j 不算在内,因为它们很便宜。

\n

对于给定方法,这可能是最有效的实现:

\n

https://play.golang.org/p/YOOSZjIWKZ_r

\n
package 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}\n
Run Code Online (Sandbox Code Playgroud)\n

但这太过分了!

\n