• Shawn 置顶
    2018-08-17
    当前变量覆盖外层变量
    
     32
  • 陈悬高
    2018-10-06
    所谓“变量的重声明”容易引发歧义,而且也不容易理解。如果没有为变量分配一块新的内存区域,那么用声明是不恰当的。在《Go 语言圣经》一书中将短声明的这种特性称为赋值。个人总结如下:

    在使用短变量声明的时候,你可能想要同时对一个已有的变量赋值,类似使用 `=` 进行多重赋值那样(如 `i, j = 2, 3`)。所以,Go 为短声明语法提供了一个语法糖(或者叫便利措施):短变量声明不需要声明所有在左边的变量。如果多个变量在同一个词法块中声明,那么对于这些变量,短声明的行为等同于*赋值*。

    比如,在下面的代码中,第一条语句声明了 `in` 和 `err`。第二条语句仅声明了 `out`,但向已有的 `err` 变量进行赋值。

    ```
    in, err := os.Open(infile)
    // ...
    out, err := os.Create(outfile)
    ```

    但是这种行为需要一些前提条件:

    * 要赋值的变量必须声明在同一个词法块中。

        如果两个变量位于不同的词法块中,短声明语法表示的仍然是“声明”而非“赋值”。此时它们就是重名的变量了,而且内层变量会“覆盖”外部变量。

    * 必须至少声明一个新变量,否则代码将不能编译通过。

        原因很简单,如果不用声明新变量而仅仅是为了赋值,那么直接使用赋值符 `=` 即可:

        ```
        f, err := os.Open(infile)
        // ...
        // f, err := os.Create(outfile) // 编译错误:没有新变量
        f, err = os.Create(outfile) // 使用普通的赋值语句即可
        ```
    展开
     1
     95
  • Andy Chen
    2018-08-17
    “你可以随意改变getTheFlag函数的内部实现及其返回结果的类型,而不用修改main函数中的任何代码。”这个说法只在你给定的例子下面成立,事实上main函数的代码已经假设getTheFlag会返回字符串,因为它在用返回值,如果getTheFlag一开始是返回某种结构体指针,main使用了这个指针指向的一系列成员,然后你再改getTheFlag返回类型看看。类型推断已经深入大多数语言,包括c++,C#,等等,但它没办法解决所谓的使用者不需要改变任何代码就能进行重构

    作者回复: 重构有很多种,有大有小啊。

    
     14
  • 玉明
    2018-08-17
    应该是没影响的,不同栈上的变量
    
     9
  • xiaolonghuster
    2018-08-17
    按照老师的demo,不能获取命令参数,只能得到默认值,改成下面这样可以:我用的是1.10.3版本,是不是版本问题

    func main() {

        var name = getTheFlag()
        
        flag.Parse()
        fmt.Printf("Hello, %v!\n", *name)
    }

    func getTheFlag() *string {

        return flag.String("name", "everybody", "The greeting object.")
    }
    展开

    作者回复: 这里确实是写错了,你改的很对,谢谢指正!我想邀请你进入本专栏的微信讨论群。你知道入群方法吗?

     2
     8
  • javaadu
    2018-08-17
    go支持类型推断;
    两种变量定义方式:var完整方式、:=短变量定义;
    重声明只可以在短变量定义中出现,并且是在多个变量声明中出现(给新变量赋值,给旧变量赋新值)
    
     6
  • Jevade
    2018-11-22
    string 是值传递,所以调用flag.String函数的时候,本身会构造一个string
    var name = *flag.String("name", "everyone", "The greeting object.")
    传递给name的是一个string的副本,和原本的string只是值一样,地址并不同,后面采用flag.Parse也就不会改变name,因此name还是默认值。
    不知道这样理解对不对?
    
     4
  • 冰激凌的眼泪
    2018-08-18
    var name = *flag.String("name", "everyone", "The greeting object.")
    这一句是不是导致name是个副本,parse后也不会变?请郝老师确认一下

    作者回复: 已经修正了。

    
     4
  • 小苹果
    2019-05-04
    老师,puzzlers\article4\q1\demo7.go里面有个隐式错误。
    var name = *flag.String("name", "everyone", "The greeting object.")
    这种方法输出的结果永远是 Hello, everyone!,不论 -name 后面是什么。
    正确的应该是:
    func main() {
        // 方式1。
        var name = flag.String("name", "everyone", "The greeting object.")
        flag.Parse()
        fmt.Printf("Hello, %v!\n", *name)
    }
    展开

    作者回复: 嗯,看到了,这确实是个问题,当初是为了不改动其他代码。我想想怎么调整一下。谢谢。

    
     2
  • lfn
    2018-08-17
    变量解析由内向外,内层找不到才回去外层找。
    
     2
  • 钟鑫
    2019-11-13
    对于变量重声明我有一点疑问,我程序中尝试了,对于重声明的变量其地址是一样的,这个还算重声明吗?

    作者回复: 当然算,这是两码事。

    
     1
  • 🐶
    2019-08-08
    作用域不同,当前变量会覆盖外层作用域的变量
    
     1
  • liyinda0000
    2018-09-07
    在article4/q1/demo7.go中在使用{方式1}中,发现name传参未能正常打印,我使用的go版本1.9.4
        解决办法: 13行 *flag.String改成flag.String;19行 fmt.Printf("Hello, %v!\n", name) 将name改成*name
        问题思考:应该是文中未深入探讨‘指针变量’的问题,flag.String()得到的是指针变量,*代表取值符,*name将指针变量中的值取出(运行&name发现为内存地址,&代表取地址符),上述问题的出现原因请您指点?另如何加入微信学习群,望早日加入组织,哈哈

    作者回复: 微信学习群已经满了,不过可以去开发者头条App搜索GoHackers并加入组织。这个组织是我个人在几年前发起的。

    
     1
  • charlesgogo01
    2018-09-03
    name := *flag.string()这儿为啥会有*,本来返回应该是个变量值,这样是传递地址吗?

    作者回复: Go里面没有传递地址这种说法。*在这里是取值操作符。

    
     1
  • 慢熊胖胖跑
    2018-08-18
    由于go是值传递,因此即使传入重名变量,一般在代码块中变量可以正常使用,但是值得改变不会引起变化,因为变量传入后,代码块中赋予了新的地址。 除非如同case3一样中传入变量的指针,然后才会使用相同的变量地址,修改变量的值。func reusevarnam1(var1 int) {    var1 = 3 + var1    fmt.Printf("Inside reusevarnam1,var1 is %d, address is %s\n", var1, &var1)}
    func reusevarnam2(var2 int) {    for var2 := 1; var2 < 3; var2++ {        fmt.Println("reusevarnam2 ...")        fmt.Printf("Inside reusevarnam2,var2 is %d, address is %s\n", var2, &var2)    }    fmt.Println("reusevarnam2")}
    func reusevarnam3(var3 *int) {    *var3 = *var3 + 100    fmt.Printf("Inside reusevarnam2,var3 is %d, address is %s\n", *var3, var3)}
    展开

    作者回复: 你这一堆❀是啥?:)

    
     1
  • 小小笑儿
    2018-08-17
    思考题:
    内部作用域的变量会覆盖外部作用域的变量,是声明一个新的变量而不是重声明,重声明只在同一个作用域生效。
    可以使用go vet -shadow显示这一类错误。

    作者回复: vet会提示有隐患,重声明和可重名变量都容易让人产生迷惑,这也是也讲到它们的原因。哦,对了,课重名变量会在下一篇讲。

    
     1
  • 后端进阶
    2018-08-17
    类型推断只能在局部生效,相当于Java的局部变量,而在函数体外的声明变量相当于Java的实例变量
    
     1
  • dlili
    2019-11-28
    :=短变量声明可以理解成var声明后再赋值,可以运行下边的程序理解

    ```

    func main() {
        err := test() //声明并赋值一个error类型的变量
        fmt.Printf("%v:%s\n",&err,err.Error())
        if true{
            err := test() //在if代码块中声明并赋值一个error类型的变量,与if外的不同,这是一个新的变量
            fmt.Printf("%v:%s\n",&err,err.Error())
        }
        if true{
            err := "error" //在if代码块中声明并赋值一个error类型的变量,与if外的不同,这是一个新的变量
            fmt.Printf("%v:%s\n",&err,err)
        }
        //err := errors.New("error") // 编译错误,此变量已经声明过
        err = errors.New("error") // 重新赋值
        fmt.Printf("%v:%s\n",&err,err.Error())
    }

    func test() (err error) {
        return errors.New("error")
    }
    ```
    展开
    
    
  • sun🍏🍌🍒🍅...
    2019-09-19
    简单事情说的复杂化 讲解的时候能不能举例
    
    
  • w
    2019-09-05
    那个方便重构的例子,与其说是类型推断带来的方便,感觉更像是接口给带来的。不知道是不是我自己的错觉。

    作者回复: 在这个小例子里没有接口的事啊。当然你要说接口有利于重构,话也没错。

     1
    
我们在线,来聊聊吧