Go从入门到精通——理解变量

理解变量

本章介绍如下内容:

  • 变量是什么?
  • 快捷变量声明。
  • 理解变量和零值。
  • 编写间断变量声明。
  • 哪种变量声明方式更好?
  • 变量作用域
  • 使用指针。
  • 声明常量。

    变量是计算机程序不可或缺的部分。

1.1 变量是什么

  变量就是值的引用,是实现程序逻辑的基石之一。在 Go 语言中,声明变量的方式有多种。Go 是一种静态类型语言, 因此声明变量时必须显式或隐式地指定其类型。

  程序清单:声明 string 变量

package main

import (
	"fmt"
)

func main() {
	var s string = "Hello world"
	fmt.Println(s)
}

  对程序解读如下:

    • 1、使用关键字 var 声明一个变量。
    • 2、这个变量名为 s。
    • 3、这个变量的类型为 string。
    • 4、赋值运算符 "=" 表示将它右边的值赋给变量。
    • 5、将字符串字面量 Hello World 赋值给变量 s。
    • 6、标准库中的 fmt 包通过变量 s 来引用其值,并将这个值传递给方法 Println。
    • 7、打印 s 的值。

  程序清单:声明变量后再给它赋值

package main

import "fmt"

func main() {
	var s string
	s = "Hello World"
	fmt.Println(s)
}

  Go 语言是静态编程语言,变量的类型很重要,因为这决定了可将什么值赋给变量。例如,对于类型为 string 的变量,不能将整数值赋给它;同理,不能将字符串赋给布尔变量。将类型不正确的值赋给变量时,将导致编译错误。现在我们举个错误赋值的例子:

  程序清单:将类型不正确的值赋给变量

package main

import (
	"fmt"
)

func main() {
	var i int
	i = "ONE"
	fmt.Println(i)
}

 1.2 快捷变量声明

  Go 支持多种快捷变量声明方式。可在一行内声明多个类型相同的变量并给它们赋值。 

  程序清单:快捷变量声明

package main

import (
    "fmt"
)

func main() {
    var s, t string = "foo", "bar"
    fmt.Println(s)
    fmt.Println(t)
}

   程序清单:以快捷方式声明类型不同的变量

package main

import (
    "fmt"
)

func main() {
    var (
        s string = "foo"
        i int    = 4
    )

    fmt.Println(s)
    fmt.Println(i)
}

   变量声明后,就不能再次声明它。虽然可以给变量重新赋值,但不能重新声明变量,否则将导致编译阶段错误。

  程序清单:变量声明后,就不能再次声明它。

package main

import (
    "fmt"
)

func main() {
    var s int = 1
    fmt.Println(s)

    var s string = "Hello World"

}

 1.3 理解变量和零值

  在 Go 语言中,声明变量时如果没有给它指定值,则变量将为默认值,这种默认值被称为零值。这不同于其他语言,因为在这些语言中,未赋值的变量的值为 nil 或 undefined。

  程序清单:演示变量的零值

package main

import (
    "fmt"
)

func main() {
    var i int
    var f float32
    var b bool
    var s string
    fmt.Println("%v %v %v %q\n", i, f, b, s)
}

  使用变量时,知道 Go 语言的这种设计决策很重要。不久之后,您可能就要检查变量是否赋值了。注意,在 Go 语言中,为确定变量是否已经赋值,不能检查她是否为 null,而必须检查它是否为默认值。由于类型 string 的零值为 "",因此对于类型为 string 的变量,要确定是否已经给它赋值,可检查其值是否为零值。

  程序清单:检查变量的值是否为零值

package main

import (
    "fmt"
)

func main() {
    var s string
    if s == "" {
        fmt.Printf("s has not bee assigned a value and is zero valued")
    }
}

 1.4 编写简短变量声明

  在函数中声明变量时,可使用更简短的方式——简短变量声明。

  程序清单:简短变量声明

package main

import (
    "fmt"
)

func main() {
    s := "Hello World"
    fmt.Println(s)
}

   对上程序清单中程序解读:

    • 声明一个名为 s 的变量。注意,这里没有指定关键字 var 和 类型。
    • 简短变量赋值语句 := 表明声明的是简短变量声明,这意味着不需要使用关键字 var,也不用指定变量的类型。同时还意味着应将 := 右边的值赋给变量。
    • 将字符串字面量 "Hello World" 赋值给变量 s。

  使用简短变量声明时,编译器会推断变量的类型,因此无需显式地指定变量的类型,这只能用在函数中使用简短变量声明。

1.5  变量声明方式

  Go 语言提供了多种变量声明方式。为完整起见,下面列出所有的变量声明方式:

var  s  string = "Hello World"
var s = "Hello World"
var t string
t = "Hello World"
u := "Hello World"

  该使用哪种方式呢?Go 对此有一定的限制——不能在函数外面使用简短变量声明。在遵循这条规则的前提下,怎么做都可以。

  当然,如何声明变量是风格的问题。在同一行内声明变量并给它赋值时,Go 语言设计者在标准库中遵循的约定如下:在函数内使用简短变量声明,在函数外省略类型。

  程序清单:惯用的变量声明

package main

import (
    "fmt"
)

var s = "Hello World"

func main() {
    i := 42
    fmt.Println(s)
    fmt.Println(i)
}

 1.6 理解变量作用域

  作用域指的是变量在什么地方可以使用,而不是变量在什么地方声明的。Go 语言使用基于块的词法作用域。词法是一个形容词,意思与语言的词汇表相关。这意味着 Go 定义了变量在什么地方可以引用,在什么地方无法引用。对于编程来说,这很有必要,因为这样可根据应用变量的位置,确定引用的是哪个变量。在 Go 语言中,块是位于一对大括号内的一系列声明和语句,但可以是空的。

  可使用通俗的语言前述概念做如下诠释:

    • 在 Go 语言中,一对大括号({})表示一个块。
    • 对于在大括号({})内声明的变量,可在相应块的任何地方访问。
    • 大括号内的大括号定义了一个新块——内部块。
    • 在内部块中,可访问外部块中声明的变量。
    • 在外部块中,不能访问在内部块中声明的变量。

  简而言之,每个内部快都可访问其外部块声明的变量,但外部块不能反问内部块声明的变量。

    • 大括号定义的程序结构和变量作用域。每对大括号都表示一个块。
    • 代码的缩进程度反映了块作用域的层级。在每个块中,代码都被缩进。
    • 在内部块总,可引用外部块中声明的变量。

  程序清单:Go语言中的词法作用域

package main

import (
    "fmt"
)

var s = "Hello World"

func main() {
    fmt.Printf("Print 's' variable from outer block %v\n", s)
    b := true

    if b {
        fmt.Printf("Printing 'b' variable from outer block %v \n", b)
        i := 42
        if b != false {
            fmt.Printf("Printing 'i' variable from outer block %v \n", i)
        }
    }
}

  注意,变量 s 不是在大括号内声明的,但可在内部块中访问它。这是因为 Go 语言将文件也视为块,所以在第一级大括号外声明的变量可以在所有块中访问。

1.7 使用指针

  值传递(pass by value)是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。

  引用传递(pass by reference)是指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数

  什么是指针?

  就是变量在内存中的地址,它所指向的值的内存地址在32位和64位机器上分别占用4和8字节;当一个指针被定义后,没有分配到任何变量时,它的默认值为nil;

  指针是另一个与变量相关且必须掌握的要素。在 Go 语言中声明变量时,将在计算机内存中给它分配一个位置,以便能够存储、修改和获取变量的值。要获取变量在计算机内存中的地址,可在变量名前加上 & 字符。

  程序清单:打印变量在内存中的地址

package main

import (
    "fmt"
)

func main() {
    s := "Hello World"
    fmt.Println(&s)
}

 

  运行代码后,打印出一个由字母和数字组成的序列,它表示变量在内存中的地址,并不是变量的值。

  将变量传递给函数时,会分配新内存并将变量的值复制到其中,这样将有两个变量实例,它们位于不同的内存单元中。一般而言,这不可取,因为这将占用更多的内存,严格地说,Go方法或函数只有一种传递方式,那就是值传递。每次将一个变量作为参数传递时,都会创建一个新的变量副本并将其传递给所调用的函数或方法。副本分配在不同的内存地址。

  同时由于存在变量的多个副本,很容易引入 Bug。考虑到这点,Go 提供了指针。指针是 Go 语言中的一种类型,指向变量所在的内存单元。要声明指针,可在变量前加上星号字符。

  取地址操作符&和取值操作符*是一对互补操作符。

  &取出内存地址,*根据内存地址取出 内存地址 实际指向的值。

  程序清单:将变量作为值传递.go

package main

import "fmt"

func showMemoryAddress(i int) {
	fmt.Println(i)
	return
}

func main() {
	i := 1

	showMemoryAddress(i)

	fmt.Println(&i)
}

  程序清单:将变量作为指针传递.go

package main

import "fmt"

func showMemoryAddress(x *int) {
	fmt.Println(x)
	return
}

func main() {
	i := 1

	showMemoryAddress(&i)

	fmt.Println(&i)
}

1.8 声明常量

  常量指的是在整个程序生命周期内都不变的值。常量初始化后,可以引用它,但不能修改它。

  程序清单:声明常量

package main

import (
	"fmt"
)

const greeting string = "Hello Word"

func main() {
	fmt.Println(greeting)
}

  我们现在试着修改常量后,运行看看:

  程序清单:修改声明后常量的值

package main

import (
	"fmt"
)

const greeting string = "Hello Word"

func main() {
	greeting = "Goodbye, cruel world"
	fmt.Println(greeting)
}

posted @ 2021-08-16 11:12  左扬  阅读(91)  评论(0编辑  收藏  举报
levels of contents