变量和作用域
对于变量,我们应该并不陌生。在前面几篇章节中,我们都有使用到了变量。
需要注意的是,声明变量除了前面我们使用的var
关键字外,还可以使用短声明
的形式:变量名:=10
,两则效果是一致的
ps:某些情况,我们只能使用短声明来形如变量:
如:在for循环的时候,我们的条件变量不能使用var声明在内部,只能在外部先声明后使用即:
package main
import (
"fmt"
)
func main() {
var count = 0 // 只能声明在外部
for count = 10; count > 0; count-- {
fmt.Printf("计数器count=%v, 小于10
", count)
}
}
如果想要实现一步到位就可以使用短声明for count : 10;count > 0 { }
同样的情况在if
和switch
分支语句中也是如此,just like -> if count := rand.Intn(10); count == 8 { }
&& switch num := rand.Intn(10); num {case 0:...}
什么是作用域:
-
这里主要说的是作用域的概念。当一个变量被声明后,它就进入了某个作用域中了, 换句话说,我们在某个作用域空间里面声明了一个变量
-
所谓变量的作用域,指的就是某个变量在一块区域里面是“可见”的,或者说是可以被访问的。唯有变量在作用域内,作用域内的其他成员才能访问它,否则编译器会报错
-
在Go中,一个作用域范围是十分明显的,一个大括号
{}
里面的范围就被称为一个作用域 -
除了大括号的作用域外,还有一个特殊作用域:包作用域
在函数外部,包的内部里面声明的变量可以认为是这个包里面所有方法的全局作用域,即所有的方法对这些变量都是具有访问权限的
需要注意的是,短声明不可以用来声明包作用域的变量package main import "fmt" var globelCount = 0 func main() { globelCount++ // 对全局变量进行+1操作 test() // test方法里面也对全局变量globelCount+1 fmt.Println(globelCount) //print 2 } func test() { globelCount++ }
作用域的优势:
作用域实现了面向对象思想中的其一特性:封装。该特性在面向对象编程的学习中会频繁出现在我们的视野中,最直接的好处就是可以在不同的作用域内使用相同的变量名
实数
实数可以说包含了我们平时所见到的所有数,在编程中我们用到比较多的是浮点数,即带小数点的数
浮点数
Go中有两种类型的浮点数类型:
float64
单精度浮点数(就是其他语言中double
类型,go的默认浮点数类型)
64位的浮点类型,占用8字节内存float32
双精度浮点数(如果要使用该类型,必须使用显式声明的方式)
32位的浮点类型,占用4字节内存
Go语言中,声明浮点数的方式有以下三种,每一种的效果是相同
package main
import "fmt"
func main() {
// 以下三种浮点型变量效果是一样的
pi1 := 3.1415926
var pi2 = 3.1415926
var pi3 float64 = 3.1415926
// var pi4 float32 = 3.1415926 // 显式声明单精度的浮点数
fmt.Print(pi1, pi2, pi3)
}
需要注意的是,除非是像第三种方式那样显示声明类型,否则只要表面值含有小数部分,那么它的类型就是float64
的类型,如height := 179.9
,这是一个float64
类型的变量
如果表面值是一个整数,想让其为浮点类型的话也需要用第三种显式声明的方式,否则它是一个整数类型变量
使用Print
或Println
打印浮点数类型的时候,会尽可能多的显示小数的个数,我们也可以使用Printf
函数 + “格式化动词”来指定小数的位数,其本质也是一个占位符(前面学习到的%v
是一个通用的占位符)
如:fmt.Printf("%3.2f
", floatNum)
格式化动词%f由两部分组成
宽度:会显示出的最少字符个数(包含小数点和小数),如果宽度大于数字的个数,那么左边会填充空格(也可以使用0填充,格式位%0x.x
),如果没有指定宽度,那么就按实际的位数进行显示
精度:小数点后面显示的位数
零值
Go语言中,每个实数类型都一个默认值,该默认值成为零值
当我们声明一个变量却未对其初始化的时,该变量的默认值就位零值
拓展
因为浮点数的精度问题(不够精确),当我们如下面比较浮点类型的时候,得到的结果是false
package main
import "fmt"
func main() {
var num = 0.1
num += 0.2
fmt.Println(num == 0.3) // false
}
但是在业务上,我们又认为是true才对,这个时候,我们可以使用下面的折中方案
fmt.Println(math.Abs(num-0.3) < 0.0001)
整数
Go语言中提供了10种整数类型,它们主要区别在于存储的范围大小以及是否有符号(可表示正数、0、负数)
在编程中,最常用的整数类型是int
,其也为默认的整数类型,也就是说
num :=1
-> var num = 1
-> var num int = 1
其三者类型是一致的
无符号整数类型的声明方式如下:
var num uint = 1
下图为go语言中有八种与架构无关的整数类型:
所谓与架构无关,就是与计算机硬件无关的意思,而我们用的int和uint则是会针对目标设备优化的类型:
- 在一些比较老的设备和树莓派2上,其二者都是32位
- 在如今较为新的计算机上,二者都为64位
ps:当一个数超过了给定整数类型的取值范围,那么就会回到最小值开始计算,即出现了环绕现象,如一个无符号整数数取值范围是255,当我们给其值256,则该变量就会等于0
上面介绍了两种数据类型,那么我们如何知道当前某个变量是什么类型呢?我们可以使用Printf
的格式化动词%T
打印一个变量的数据类型
拓展:
- 在go中要使用十六进制表示数值,可以在数值的前面加上0x前缀如
var num = 0x00
,若要打印十六进制的数,则可以使用格式化动词%x
,其也可以和浮点数格式动词一样指定填充宽度%2x
- 如要打印bit,则使用
%b
这个格式化动词%08b
表示:8位二进制数,宽度不足以0填充 - 如要获取一个数值类型的最大值和最小值,则可以使用math包中的Max方法
思考:
-
浮点类型可以存储很大的数,但是精度不高;整型很精确但取值范围有限,如果遇到很大的数但有要求很精确怎么办?
- 首先可以考虑int64存储
- 如还不够大可以用uint64,无符号数可以存储更大范围的数值
- 还不够的话可以使用
big
包- 对于较大的整数(超过10e18):我们可以使用
big.Int
类型
可以使用big.NewInt(123456)
将一个整数转化为big.Int
类型,但往往我们使用big.Int
都会超过go的最大整数类型int64
,这块就会报错了。此时咋整呢?
可以先实例化一个big.Int
类型var num = new(big.Int)
,然后再给类型赋值,赋值方式是使用实例的SetString方法
,该方法接收两个参数,第一而参数是一个数值的字符串形式(字符串可以无限长),第二个字段是数值的进制,如num.SetString("888888888888888888888888",10)
,就表示一个十进制的big.Int
类型数值
ps:除了SetString
方法,big.Int
常见的还有Div
方法,其表示是数字相除得到的big.Int
类型的数值 - 对于任意精度的浮点数类型,我们可以使用
big.Float
类型 - 对于分数,可以使用
big.Rat
类型
- 对于较大的整数(超过10e18):我们可以使用
ps:如果一个数使用指数形式表示且未给其指定类型的话,如:
var num = 88e11
那么该数值Go默认是float64
类型
拓展:
在go中,对于变量Go会推断其类型,但是常量不会。常量是可以untyped
类型,即无类型,使用无类型的常量就没有范围限制了!
常量使用const关键字来声明,程序里每个字面值都是常量,这意味着:比较大的数值可以直接作为字面值使用,针对字面值和常量的计算,是在编译阶段完成的
go的编译器是go编写的,无类型的数值字面值就是由big
包所支撑的,所以只要能够容纳下,那么常量就可以赋值给变量
尽管编译器用big包处理无类型的数值常量,但是常量和big.Int
类型的值两者间是不能互换