2017-03-04 84 views
2

我写了一个小片段,以递归走一个目录,返回基于扩展名的文件([]string)的一个切片。它似乎工作,但我不能完全明白指针后面的想法以及如何正确使用它。试图把握Go的指针

package main 

import (
    "path/filepath" 
    "io/ioutil" 
    "fmt" 
) 

// aggregator slice should hold the result (a slice of filepaths) 
// dir is the current directory being listed 
// exts is a slice of extensions that should be included in the result 
func recurse(aggregator *[]string, dir string, exts *[]string) []string { 
    files, _ := ioutil.ReadDir(dir) 
    for _, file := range files { 
     // current filepath 
     path := filepath.Join(dir, file.Name()) 

     // if directory, recursively analyze it 
     if file.IsDir() { 
      *aggregator = recurse(aggregator, path, exts) 

     // else check if file is of proper extension and add it to aggregator 
     } else { 
      for _, ext := range *exts { 
       if (filepath.Ext(path) == ext) { 
        *aggregator = append(*aggregator, path) 
        break 
       } 
      } 
     } 
    } 
    return *aggregator 
} 

func getTemplates(templatesDir string, templatesExtensions ...string) { 
    templates := recurse(&[]string{}, templatesDir, &templatesExtensions) 

    // for testing purposes just print filepaths 
    for _, t := range templates { 
     fmt.Printf("%s\n", t) 
    } 
} 


func main() { 
    getTemplates("./templates", ".tmpl", ".html") 
} 

的主要问题是使用*aggregatoraggregator *[]string),&[]string{}*extsexts *[]string)。我来自javascript世界,其中每个对象基本上都是一个指针,并且您只能显式复制对象和数组。另一方面,在这里,如果我没有使用指针(*aggregator等),这些对象似乎会在每个函数迭代中被复制。

我是正确的这种方法还是我错过了什么?

回答

2

虽然该可视化是没有专门关于围棋(这是关于Java的),但它的实际可视化指针和值的使用完美的一(1): enter image description here

正如你看到一个指针,实际上并不包含的任何数据,但它指向数据所在的位置。因此,通过指针对数据进行的任何修改实际上都是在数据本身上执行的。但数据不一定驻留在我们的指针正在使用的地方。

当我们需要指针时,会有不同的情况。例如,当您想要修改某个特定位置的实际值(而不是传递这些值)时,或者当您的数据太大以至于成本太高而无法发送实际内容时。您可以使用指向这些大数据的/ some指针,每个有指向数据指针的每个人(例如每个函数)都可以读取或修改它。正如我们所说的,我们可以根据需要获得同样数量的指针。所以可能会有很多指向同一个数据的指针。这些指针的值是相同的;这是源数据(对象)的地址。

忘记在Go中添加切片会被指针/引用传递,但不能修改切片(添加/删除项目)。你可以修改它的项目。如果您需要修改切片本身,则必须使用指向切片的指针。否则,如果你只是将它作为一些值的容器传递,你不必担心,因为它们不会每次都被复制。

(1)Source

+0

明白了,谢谢。因此,举例来说,如果我有一个函数* * *返回值(就像我的例子),指针可能会被省略,但如果我有一个函数*不*返回值,但想修改切片(添加/删除项目),我必须使用指针。 –

0

在go切片是引用类型,连同地图和指针(我很确定字符串也不会让我对它进行尽管:)参见here对此进行讨论)。所以这些特定的类型会自动按引用传递。所以实际变量本身就是一个指针,其值就是引用地址。 所以你的情况这将是安全的,可能优选改变aggregator *[]stringaggregator []string和您的数据将不会复制只是一个引用传递。当然,随着这一变化,您需要更改之前取消引用的所有代码,例如aggregator

// Change this line 
*aggregator = append(*aggregator, path) 
// To this 
aggregator = append(aggregator, path) 

这样做很可能会从基于C的语言,其中的干数组只是指针分配的内存开始的理由。

注意:所有其他类型(包括结构体)不遵循此模式(接口是另一种异常有趣的类型read)。此外,代码看起来像filepath.Walk()可以大大简化。

对于位的更多信息虽然越来越多的针对地图显示this博客文章。

+0

除了追加()返回一个值,因为它可能会改变底层数组,而不仅仅是切片,而这种改变不会在呼叫者被反射如果切片也不会被传递通过引用或由值返回(如追加)。 – Adrian