2014-07-02 100 views
3

我正在查看代碼示例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) 
    } 

爲什麼「名稱」變量在循環內初始化並且不在循環之外? (見下文)。在每個循環中重新初始化它不是沒有那麼高效嗎?

//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) 
     } 
+0

'name'在循環之前被聲明,否則它將被限制到if塊並且不在該塊之外的範圍內(閱讀:以後不能使用它)。循環不會重新初始化它;它只是重新使用它。塊(循環)位於變量聲明之後。 – elithrar

+0

只是想提一下''name''實際上是在循環之內聲明的(在sql文檔中)。在我看來,我錯過了塊範圍的東西。我不知道''if''(也可能是''for'')塊具有這種隔離屬性 – hey

+0

@hey:它包含在[The Go Programming Language Specification](http://golang.org/ref/spec):[Blocks](http://golang.org/ref/spec#Blocks)。 – peterSO

回答

3

除非你已經確定的分配是一個性能瓶頸,我不會考慮這樣一個不成熟的優化。畢竟,它甚至可能沒有什麼區別,所以最好在可讀性/可維護性方面犯錯。

一般來說,我建議使用最小範圍的變量是有道理的。如果它們被分配了堆棧,那麼它們將非常便宜 - 假設空間可用,它可能只是將變量初始化爲零或其初始值。堆棧分配的變量在一個循環中的作用域可能最終每次通過循環時都會有相同的內存位置,所以將它們移出它們並沒有多少好處。

就是這樣,當一個變量被分配到堆棧上時,並不總是很明顯。如果編譯器確定傳遞給row.Scan的指針可能會被保留在函數調用之後(即它的轉義爲),那麼name將被分配到堆上,即使它已被定義爲var

同樣,如果轉義分析確定該變量不能轉義,則創建帶有new的字符串變量的版本可能決定將其放置在堆棧上。

+0

這有點煩人。例如,我有一些準備好的語句,所以如果使用不當(即緩存),性能將得到改善。但是,如果在每個循環中初始化一個新的連接/語句使其無用。我想在循環中聲明它們,但有時我會得到「連接太多」的錯誤,所以我只能假設Go在每個循環中初始化它們,因此對於SQL示例的混淆。 – hey

+0

如果你想在循環的迭代之間使用相同的值,那麼在循環之外聲明變量是有意義的。在你的問題,但你有一個變量只用於循環。 –

相關問題