变量常量

2023/1/15

# 变量

Go 语言变量名由字母、数字、下划线组成,其中首个字符不能为数字。

# 变量声明

  1. 第一种,指定变量类型,如果没有初始化,则变量默认为零值。通用的变量声明格式: 式:
var a int = 10

这个变量声明分为四个部分:

  • var是修饰变量声明的关键字;
  • a为变量名;
  • int为该变量的类型;
  • 10是变量的初值。
  1. 第二种,根据值自行判定变量类型。

  2. 第三种,如果变量已经使用 var 声明过了,再使用 := 声明变量,就产生编译错误,格式:

v_name := value

# 多变量声明

多变量声明是针对类型相同多个变量, 非全局变量

var vname1, vname2, vname3 type
vname1, vname2, vname3 = v1, v2, v3
var vname1, vname2, vname3 = v1, v2, v3 // 和 python 很像,不需要显示声明类型,自动推断
vname1, vname2, vname3 := v1, v2, v3 // 出现在 := 左侧的变量不应该是已经被声明过的,否则会导致编译错误

// 这种因式分解关键字的写法一般用于声明全局变量
var (
    vname1 v_type1
    vname2 v_type2
)

# 变量注意事项

  • 变量所绑定的内存区域是要有一个明 确的边界的。
    也就是说,通过这样一个变量,我们究竟可以操作4个字节内存还是8个字节内 存,又或是256个字节内存,编程语言的编译器或解释器需要明确地知道。

  • 声明聚类与就近原则
    就近原则,也就是说 我们尽可能在靠近第一次使用变量的位置声明这个变量。就近原则实际上也是对变量的作用 域最小化的一种实现手段,如标准库http包中的代码就是这样:

// $GOROOT/src/net/http/request.go
var ErrNoCookie = errors.New("http: named cookie not present")
func (r *Request) Cookie(name string) (*Cookie, error) {
 for _, c := range readCookies(r.Header, name) {
 return c, nil
 }
 return nil, ErrNoCookie
}

# 变量分类

通常来说,Go语言的变量可以分为两类:

  • 一类称为包级变量(package varible),也就是在 包级别可见的变量。如果是导出变量(大写字母开头),那么这个包级变量也可以被视为全 局变量;
  • 另一类则是局部变量(local varible),也就是Go函数或方法体内声明的变量,仅在 函数或方法体内可见。

# 包级变量的声明形式

包级变量只能使用带有var关键字的变量声明形式,不能使用短变量声 明形式,但在形式细节上可以有一定灵活度。

  • 第一类:声明并同时显式初始化 var varName = initExpression
    尽量保证声明一致性,,这 样能统一接受默认类型和显式指定类型这两种声明形式。
var (
a = 13
b = int32(17)
f = float32(3.14)
)

  • 第二类:声明但延迟初始化 如:var a int32, 虽然没有显式初始化,Go语言也会让这些变量拥有初始的“零值”。如果是自定义的类型,尽量保证它的零值是可用的。 还有一个注意事项,就是声明聚类与就近原则。

# 局部变量的声明形式

和包级变量相比,局部变量又 多了一种短变量声明形式,这是局部变量特有的一种变量声明形式,也是局部变量采用最多 的一种声明形式。

  • 第一类:对于延迟初始化的局部变量声明,我们采用通用的变量声明形式
  • 第二类:对于声明且显式初始化的局部变量,建议使用短变量声明形式 对于不接受默认类型的变量,我们依然可以使用短变量声明形式,只是在":="右侧要做一个显 式转型,以保持声明的一致性:
a := int32(17)
f := float32(3.14)
s := []byte("hello, gopher!")

并且,尽量在分支控制时使用短变量声明形式;

但是如果你在声明局部变量时遇到了适合聚类的应用场 景,你也应该毫不犹豫地使用var声明块来声明多于一个的局部变量,如标准库net包中resolveAddrList方法:

// $GOROOT/src/net/dial.go
func (r *Resolver) resolveAddrList(ctx context.Context, op, network, 
 addr string, hint Addr) (addrList, error) {
 ... ...
 var (
 tcp *TCPAddr
 udp *UDPAddr
 ip *IPAddr
 wildcard bool
 )
 ... ...
}

# 常量

常量是一个简单值的标识符,在程序运行时,不会被修改的量。

常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。

常量的定义格式:

const identifier [type] = value
// 你可以省略类型说明符 [type],因为编译器可以根据变量的值来推断其类型。
//显式类型定义: 
const b string = "abc"
//隐式类型定义: 
const b = "abc"
//多个相同类型的声明可以简写为:
const c_name1, c_name2 = value1, value2

# 零值

Go规范定义的内置原生类型的默认 值(即零值)

数据类型 默认值
int 0
float 0.00000
string ""
结构体 根据结构体内部的基础数据类型进行初始化赋值
数组(切片) 空数组
指针 nil

# 代码块与作用域

变量遮蔽是Go开发人员在日常开发工作中最容易犯的编码错误之一。 要想彻底保证不出现变量遮蔽问题,我们需要深入了解代码块与作用域这两个概 念以及其背后的规则。

# 避免变量遮蔽的原则

一个变量的作用域起始于其声明所在的代码块,并且可以一直扩展 到嵌入到该代码块中的所有内层代码块,而正是这样的作用域规则,成为了滋生“变量遮蔽 问题”的土壤

  • 显式代码块
    由两个肉眼可见的且配对的大括号包裹起来 的,我们称这样的代码块为显式代码块(Explicit Blocks)
  • 隐式代码块
    顾名思义,隐式代码块(Implicit Block)没有显式代码块那样的肉眼可见的配对大括号包裹,我们无法通过大括号来识别隐式代码块。