【问题标题】:When should you initialize a new variable and when you should not?什么时候应该初始化一个新变量,什么时候不应该?
【发布时间】:2014-07-03 15:01:19
【问题描述】:

我正在查看代码示例sql.query,我对变量的初始化方式有点困惑。据我了解 var 关键字初始化变量,但如果您已经有这样的变量,最好“重用”它而不是重新初始化它。我知道我可能误解了 golang 规范,所以我希望这个问题能帮助我(也许还有其他人)做对。

  rows, err := db.Query("SELECT name FROM users WHERE age=?", age)
    if err != nil {
            log.Fatal(err)
    }
    defer rows.Close()
    for rows.Next() {
            var name string
            if err := rows.Scan(&name); err != nil {
                    log.Fatal(err)
            }
            fmt.Printf("%s is %d\n", name, age)
    }
    if err := rows.Err(); err != nil {
            log.Fatal(err)
    }

为什么“name”变量是在循环内初始化而不是在循环外? (见下文)。在每个循环上重新初始化它的性能是否会降低?

//how I would do this
  rows, err := db.Query("SELECT name FROM users WHERE age=?", age)
    if err != nil {
            log.Fatal(err)
    }
    defer rows.Close()
    var name string //outside the loop
    for rows.Next() {

            if err := rows.Scan(&name); err != nil {
                    log.Fatal(err)
            }
            fmt.Printf("%s is %d\n", name, age)
    }
    if err := rows.Err(); err != nil {
            log.Fatal(err)
    }

甚至更好地使用指针

      rows, err := db.Query("SELECT name FROM users WHERE age=?", age)
        if err != nil {
                log.Fatal(err)
        }
        defer rows.Close()
        name := new(string) //pointer outside the loop
        for rows.Next() {

                if err := rows.Scan(name); err != nil {
                        log.Fatal(err)
                }
                fmt.Printf("%s is %d\n", name, age)
        }
        if err := rows.Err(); err != nil {
                log.Fatal(err)
        }

【问题讨论】:

  • name 在循环之前声明,否则它将被限定为 if 块并且不在该块之外的范围内(阅读:之后不能使用它)。循环不会重新初始化它;它只是重复使用它。块(循环)在变量声明之后。
  • 只想提一下 name 实际上是在循环中声明的,而不是之前(在 sql 文档中)。在我看来,我错过了块范围的事情。我不知道if(也许还有for)块有这种隔离属性

标签: performance go


【解决方案1】:

除非您确定分配是性能瓶颈,否则我不会考虑过早的优化。毕竟,它甚至可能没有什么不同,所以最好在可读性/可维护性方面犯错。

一般来说,我建议对变量使用最小的范围,这是有意义的。如果它们是堆栈分配的,那么它们将非常便宜——假设空间可用,它可能只涉及将变量初始化为零或其初始值。循环内的堆栈分配变量也可能在每次循环中都使用相同的内存位置,因此将它们移出并没有太多好处。

话虽如此,变量何时分配到堆栈上并不总是很明显。如果编译器决定传递给row.Scan 的指针可能在函数调用之后保留(即,它转义),那么name 将在堆上分配,即使它已经被用var定义。

同样,如果转义分析确定变量没有转义,则创建带有new 的字符串变量的版本可能会决定将其放入堆栈。

【讨论】:

  • 这有点烦人。例如,我有一些准备好的语句,所以如果使用不正确(即缓存),性能肯定会得到提高。但是,如果在每个循环上初始化一个新的连接/语句,这会使它们变得无用。我想在循环中声明它们,但有时我会收到“连接太多”的错误,所以我只能假设 Go 在每个循环上初始化它们,从而混淆 go SQL 示例。
  • 如果你想在循环的迭代之间使用相同的值,在循环之外声明变量是有意义的。不过,在您的问题中,您有一个仅在循环中使用的变量。
猜你喜欢
  • 2011-05-16
  • 1970-01-01
  • 1970-01-01
  • 2017-05-10
  • 1970-01-01
  • 2013-08-17
  • 2020-07-16
  • 2016-05-09
  • 1970-01-01
相关资源
最近更新 更多