Go语言基础之变量和常量

1. 变量

标识符

在编程语言中标识符就是程序员定义的具有特殊意义的词,比如变量名、常量名、函数名等等。 Go语言中标识符由字母数字和_(下划线)组成,并且只能以字母和_开头。 举几个例子:abc, _, _123, a123

关键字和保留字

关键字是指编程语言中预先定义好的具有特殊含义的标识符。 关键字和保留字都不建议用作变量名。

Go语言中有25个关键字:

break        default      func         interface    select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var

此外,Go 语言中还有 37 个保留字。

Constants:    true  false  iota  nil

Types: int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
float32 float64 complex128 complex64
bool byte rune string error

Functions: make len cap new append copy close delete
complex real imag
panic recover

变量的声明

Var完整声明

声明开头是一个字母( Unicode 中的字符即可)或下划线,后面可以跟任意数量的字符 、 数字和下划线,并区分大小写 。 如 x和 X是不同的变量名称

使用var关键字是Go最基本的定义变量方式,Go把变量类型放在变量名后面。

var 变量名称 数据类型 [ = 变量初始值]

声明了一个 int 类型的变量,名字为 age的变量如下所示

var age int // 变量声明

注意 GO声明的变量就会立即分配内存空间,也就意味着会被默认初始化值

如果一个变量未被赋值,Go 会自动地将其初始化,赋值该变量类型的零值(Zero Value)。上面的声明是没有对age变量赋值,int 类型默认值是0, 因此age就被赋值为 0。

package main
import "fmt"
func main() {
var age int // 变量声明
fmt.Println("my age is", age)
age = 1 // 赋值
fmt.Println("my age is", age)
age = 2 // 赋值
fmt.Println("my new age is", age)
}

上面的程序会有如下输出:

my age is  0  
my age is 1
my new age is 2

类型推断(Type Inference)

如果变量有初始值,那么 Go 能够自动推断具有初始值的变量的类型。因此,如果变量有初始值,就可以在变量声明中省略 type

如果变量声明的语法是 var name = initialvalue,Go 能够根据初始值自动推断变量的类型。

在下面的例子中,你可以看到在第 6 行,我们省略了变量 ageint 类型,Go 依然推断出了它是 int 类型。

package main

import "fmt"

func main() {
var age = 9 // 可以推断类型
fmt.Println("my age is", age)
}

声明变量的同时可以给定初始值。

var 变量名称 变量类型 = 变量初始化值

eg:

package main

import "fmt"

func main() {
var age int = 29 // 声明变量并初始化
fmt.Println("my age is", age)
}

在上面的程序中,age 是具有初始值 29 的 int 类型变量。如果你运行上面的程序,你可以看见下面的输出,证实 age 已经被初始化为 29。

my age is 29

:=简短声明

Go支持一种用了 := 声明变量的形式,称为短变量声明(Short Hand Declaration)

name := initialvalue

不过短变量声明有一个限制,那就是它适用于函数 / 方法内部或是 if、for 和 switch 的初始化语句中;在函数外部使用则会无法编译通过,所以一般用var方式来定义全局变量。

package main

import "fmt"

func main() {
name, age := "xiang", 18 // 简短声明
fmt.Println("my name is", name, "age is", age)
}

运行上面的程序,可以看到输出为 my name is xiang age is 18

简短声明要求 := 操作符左边的所有变量都有初始值。下面程序将会抛出错误 cannot assign 1 values to 2 variables,这是因为 age 没有被赋值。

package main

import "fmt"

func main() {
name, age := "naveen" //error
fmt.Println("my name is", name, "age is", age)
}

简短声明的语法要求 := 操作符的左边至少有一个变量是尚未声明的。下面的程序中a,b 已经被声明 所以再次声明ab是错误的

package main
import "fmt"

func main() {
a, b := 2, 3 // 声明a和b
fmt.Println("a is", a, "b is", b)
a, b := 4, 5 // 错误,没有尚未声明的变量
}

上面运行后会抛出 no new variables on left side of := 的错误,这是因为 a 和 b 的变量已经声明过了,**:=** 的左边并没有尚未声明的变量。

变量可以被函数返回值时进行赋值。下面程序中

package main

import (
"fmt"
"math"
)

func main() {
a, b := 1.0, 2
c := math.Min(a, float64(b))
fmt.Println("minimum value = ", c)
}

在上面的程序中,c 的值是math.Min函数返回的结果,表示 a 和 b 的最小值。打印结果

minimum value =  1

同时要区分好简短声明的是变量只能在函数内部使用,下面的a 分为全局和本地变量 需要分清楚

package main

import "fmt"

var a int

func foo() (int, int) {
return 10, 20
}

func bar() {
fmt.Printf("global a = %d\n", a)
}

func main() {
a, _ := foo()
fmt.Printf("local a = %d\n", a)
bar()
}

上面的程序执行结果是

local a = 10
global a = 0

由于 Go 是强类型(Strongly Typed)语言,因此不允许某一类型的变量赋值为其他类型的值。下面的程序会抛出错误 cannot use “naveen” (type string) as type int in assignment,这是因为 age 本来声明为 int 类型,而我们却尝试给它赋字符串类型的值。

package main

func main() {
age := 29 // age是int类型
age = "naveen" // 错误,尝试赋值一个字符串给int类型变量
}

多个变量

Go 能够通过一条语句声明多个变量。声明多个变量的语法是 :

var 变量名1, 变量名2 <变量类型> = 初始值1,初始值2
package main

import "fmt"

func main() {
var a, b = 2, 3
fmt.Printf("a=%d b=%d\n", a, b)
a, b = b, a
fmt.Printf("a=%d b=%d\n", a, b)
}

上面的程序将会打印:

a=2 b=3
a=3 b=2

在有些情况下,我们可能会想要在一个语句中声明不同类型的变量。其语法如下:

var (  
name1 = initialvalue1,
name2 = initialvalue2
)

使用上述语法,下面的程序声明不同类型的变量。

package main

import "fmt"

func main() {
var (
name = "naveen"
age = 29
height int
)
fmt.Println("my name is", name, ", age is", age, "and height is", height)
}

这里我们声明了 string 类型的 name、int 类型的 age 和 height(我们将会在下一教程中讨论 golang 所支持的变量类型)。运行上面的程序会产生输出

my name is naveen , age is 29 and height is 0

_忽略变量

_(下划线)是个特殊的变量名,任何赋予它的值都会被丢弃。在这个例子中,我们将值2赋予b,并同时丢弃1:

_, b := 1, 2
  • 包导入
    主要是调用init 函数(比如数据库驱动的注册)
import (
_ "github.com/mydemo/dbdriver"
)
  • 返回值
    主要是忽略某个值
_,err := callFunc()
  • 类型约定
    比如判断某个struct是否实现接口的方法
    比如判断 type T是否实现了I,用作类型断言,如果T没有实现借口I,则编译错误.
type T struct{}
var _ I = T{}

其中 I为interface

2. 常量

定义

关键字 const 被用于表示常量,常量用于存储不会改变的数据,常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。常量存储在程序的只读段里(.rodanat section)

const identifier [type] = value

相对于变量,常量是恒定不变的值,多用于定义程序运行期间不会改变的那些值。 常量的声明和变量声明非常类似,只是把var换成了const,常量在定义的时候必须赋值。

// 单独声明
const pi = 3.1415
const e = 2.7182
// 批量声明
const (pi = 3.1415; e = 2.7182;)

// const同时声明多个常量时,如果省略了值则表示和上面一行的值相同。 例如:
const (n1 = 100; n2; n3;)
上面示例中,常量n1、n2、n3的值都是100

常量不能再重新赋值为其他的值

const a = 55 // 允许
a = 89 // 不允许重新赋值

常量的值会在编译的时候确定。因为函数调用发生在运行时,所以不能将函数的返回值赋值给常量。

package main

import (
"fmt"
"math"
)

func main() {
fmt.Println("Hello, playground")
var a = math.Sqrt(4) // 允许
const b = math.Sqrt(4) // 不允许
}

在上面的程序中,因为 a 是变量,因此我们可以将函数 math.Sqrt(4) 的返回值赋值给它(我们将在单独的地方详细讨论函数)。

b 是一个常量,它的值需要在编译的时候就确定。函数 math.Sqrt(4) 只会在运行的时候计算,因此 const b = math.Sqrt(4) 将会抛出错误 error main.go:11: const initializer math.Sqrt(4) is not a constant)

枚举

常量还可以用作枚举, 如下数字 0、1 和 2 分别代表未知性别、女性和男性

const (
Unknown = 0
Female = 1
Male = 2
)

iota

预声明标识符iota,它默认开始值是0,一组多个常量同时声明时其值逐行递增, iota常用来表示自增的枚举变量,专门用来初始化常量。

ota是go语言的常量计数器,只能在常量的表达式中使用。

  • iota在const关键字出现时将被重置为0。const中每新增一行常量声明将使iota计数一次(iota可理解为const语句块中的行索引)。 使用iota能简化定义,在定义枚举时很有用。

  • 跳过和插队只要是新增了一行都会计数。

  • 多个常量定义在一行(iota的值是相等的)。

  • 注意:省略了值则表示和上面一行的值相同,只是增一行会记一次数。

举个例子:

const (
n1 = iota // 0 - 被重置为0
n2 // 1 - 后续开始计数
_ // 使用_跳过某些值
n4 // 3
n5 = 100 // n5为100是中间插队
n6 = iota // 5
n7, n8 = iota + 8 // 6, 14 - 多个常量定义在一行(iota的值是相等的)
)

/* 定义数量级 (这里的<<表示左移操作,1<<10表示将1的二进制表示向左移10位,也就是由1变成了10000000000,
也就是十进制的1024。同理2<<2表示将2的二进制表示向左移2位,也就是由10变成了1000,也就是十进制的8。)*/
const (
_ = iota // iota=0; 值为0 - 不存入内存
KB = 1 << (10 * iota) // iota=1; 值为1左移10位(二进制1后面加10个零 转为十进制为1024)
MB = 1 << (10 * iota) // iota=2; 值为1左移20位(二进制1后面加20个零 转为十进制为1024的2次方)
GB = 1 << (10 * iota) // iota=3; 值为1左移30位(二进制1后面加30个零 转为十进制为1024的3次方)
TB = 1 << (10 * iota) // iota=4; 值为1左移40位(二进制1后面加40个零 转为十进制为1024的4次方)
PB = 1 << (10 * iota) // iota=5; 值为1左移50位(二进制1后面加50个零 转为十进制为1024的5次方)
)

参考感谢
Go基础——基础语法 · 语雀 (yuque.com)
变量和常量 · 语雀 (yuque.com)
指令执行&变量 · 语雀 (yuque.com)