www.zhblog.net

变量定义、常数定义


变量(Variable)是存储值的位置,变量定义可以给予识别名称(Identifier)、类型与初始化值,在 Go 中写下的103.14true"Justin"等称之为常数(Constant),常数定义可给予这些常数识别名称。

基本变量定义

要在 Go 中进行变量定义有多种形式,使用var是最基本的方式。例如,定义一个x变量,类型为int,初始化值为10

var x int = 10

这么一来,从x这个位置开始,存储了int长度的值 10,在定义变量时,类型是写在名称之后。你也可以同时创建多个变量并指定初值:

var x, y, z int = 10, 20, 30

这样的话,xyz的类型都是int,值分别是102030。如果定义多个变量时,想要指定不同的类型,可以使用批量定义:

var (
    x int = 10
    y string = "Justin"
    z bool = true
)

如果定义变量时指定了类型,但未指定初值,那么编辑器会提供默认初值,例如:

var (
    a bool        
    b int32
    c float32
    d string
    e complex128
)

在上面的定义中,abcde的值分别会是false00.0""0 + 0i。在 Go 中,定义了变量,程序中却没有取用的动作,那么会发生 declared and not used 的编译错误。

自动推断类型

在 Go 中定义变量并指定值时,可以不用提供类型,由编译器自动推断类型,例如:

var x = 10

上头的x类型会是int,而底下的定义:

var x, y, z = 10, 3.14, "Justin"

xyz的类型分别会是intfloat64string,批量定义时也可以自动推断类型,例如:

var (
    x = 10        // int 类型
    y = 3.14      // float32 类型
    z = "Justin"  // string 类型
)

短变量定义

在函数中,想要定义变量值的场合,可以使用短变量定义,例如:

x := 10
y := 3.14
z := "Justin"

如果x是首次定义,就等于是定义变量并指定值。上例也可以写成一行:

x, y, z := 10, 3.14, "Justin"

由于 Go 的函数外,每个语法都必须以关键字开始,因此短变量定义不能在函数外使用。

var定义的变量名称不可重复,然而,短变量定义时,若同一内联有新定义了另一变量,就可以重复定义已存在的变量,例如,以下是合法的,因为使用:=时有一个新的y变量:

var x = 10
x, y :=  20, 30

此时,并没有创建一个新的x变量,只是将新值指定给x而已。

由于短变量定义可以同时定义变量并指定值,因此对于底下这类需求:

package main

import "fmt"

func main() {
    var x = true
    if x {
        fmt.Println(x)
    }
}

在上例,x的范围是整个main,若改为底下,范围就只会是if区块:

package main

import "fmt"

func main() {
    if x := true; x {
        fmt.Println(x)
    }
}

类似地,for之类的语法,也常运用短变量定义。

(在数学上A := B的写法,涵义是借由 B 来定义 A,例如数学上若已经定义x以及f(x)x := f(x)表示用旧的x定义新的x,这反而像是程序语言中的x = f(x)指定的概念,当然,数学上的符号与程序语言中的符号是有出入的,Go 在这边只是借用了:=来作为另一种变量定义符号。)

交换变量值

在 Go 中,要交换两变量的值很简单,例如底下的程序执行过后,x会是20,而y会是10

var x = 10
var y = 20
x, y = y, x

基本常数定义

如一开始谈到的,在 Go 中写下的103.14true"Justin"等称之为常数(Constant),常数定义可给予这些常数识别名称,要给予名称时使用的是const关键字,例如:

const x = 10

正如〈认识预定义类型〉中谈过的,10 会是一个整数常数,不过类型未定,如果要定义一个常数的类型,可以使用int32()int64()之类的函数进行类型转换,或者是在使用const定义常数名称时指定类型,例如:

const x int32 = 10

这边的10就是int32类型了,注意,这边的x并不是一个变量,而是一个识别名称罢了,因此,会说x常数的类型是int32,而不能说x变量的类型是int32

如果有多个常数要定义,也可以批量定义,例如:

const (
    x = 10
    y = 3.14
    z = "Justin"
)

再次地,xyz分别是未定类型的整数、浮点数与字符串常数(而不是intfloat64string这三个 Go 中定义的类型),如果你想要让他们为已定义类型的整数、浮点数与字符串常数,可以在定义时指定类型:

const (
    x int = 10
    y float32 = 3.14
    z string = "Justin"
)

由于常数并非变量,因此,定义了常数并不一定要用到,底下的程序不会发生错误:

package main

import "fmt"

func main() {
    const (
        x = 10
        y = 3.14
        z = "Justin"
    )

    fmt.Println(x)
    fmt.Println(y)
}

常数表达式

由于常数可以是未定类型,因此一个有趣的地方就是,像2 + 3.015 / 415 / 4.0这样的常数表达式,该怎么在编译时期决定它们的值?答案是根据表达式中的常数运算数是整数、rune(单引号括住的常数)、浮点数或复数来决定,如果表达式中包括了越后面的常数,就会用它来决定。

因此,2 + 3.0会是未定类型的浮点数5.015 / 4会是未定类型的整数3,然而,15 / 4.0,会是浮点数类型的3.75,在规格书的〈Constant expressions〉中,列出了说明以及一些范例,例如:

const a = 2 + 3.0          // a == 5.0   (untyped floating-point constant)
const b = 15 / 4           // b == 3     (untyped integer constant)
const c = 15 / 4.0         // c == 3.75  (untyped floating-point constant)
const Θ float64 = 3/2      // Θ == 1.0   (type float64, 3/2 is integer division)
const Π float64 = 3/2.     // Π == 1.5   (type float64, 3/2. is float division)
const d = 1 << 3.0         // d == 8     (untyped integer constant)
const e = 1.0 << 3         // e == 8     (untyped integer constant)
const f = int32(1) << 33   // illegal    (constant 8589934592 overflows int32)
const g = float64(2) >> 1  // illegal    (float64(2) is a typed floating-point constant)
const h = "foo" > "bar"    // h == true  (untyped boolean constant)
const j = true             // j == true  (untyped boolean constant)
const k = 'w' + 1          // k == 'x'   (untyped rune constant)
const l = "hi"             // l == "hi"  (untyped string constant)
const m = string(k)        // m == "x"   (type string)
const Σ = 1 - 0.707i       //            (untyped complex constant)
const Δ = Σ + 2.0e-4       //            (untyped complex constant)
const Φ = iota*1i - 1/1i   //            (untyped complex constant)

现在,应该能明白,〈认识预定义类型〉中math.MaxInt64若不加上int64,何以会 overflow 的错误了。

附带一提的是,在 Go 中,模块中定义的名称若要能在模块外可见,必须是首字大写,而对于像math.MaxInt64这类的公用常数,可以定义在一个 .go 文件之中,例如math.MaxInt64,就是定义在一个const.go之中。

使用 iota 枚举

如果要需要枚举一些常数时,可以使用iota,它每遇到一次const,就会重置为0,若它在批量常数定义中使用时,第一次出现时的默认值是0,每出现一次就递增1,例如:

const (
    x = iota   // 0
    y = iota   // 1
    z = iota   // 2
 )

因为const批量定义时,若后面的值没写出,会使用前一个值设定,例如:

const (
    x = 1
    y      // 1
    z      // 1
 )

因此,如果是连续的枚举,只要写一次iota就可以了,这表示后续的值,也都使用iota,结果就是:

const (
    x = iota   // 0
    y          // 1
    z          // 2
 )

其实也可以这么写来枚举常数,只是比较麻烦:

const x, y, z = iota, iota, iota




展开阅读全文

评论

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 心情