基本结构和基本数据类型
常量
关键字const
定义;存储的数据只可以是布尔、数字(整数、浮点、复数)和字符串
// type可省略,编译器自动推断
const identifier [type] = value
一个没有指定类型的常量被使用时,会根据其使用环境而推断出它所需要具备的类型(必需能够在编译期
确定)
(未定义类型的常量会在必要时根据上下文来获取相关类型)
数字型的常量是没有大小和符号的,且可以使用任何精度而不会导致溢出
当常量赋值给一个精度过小的数字型变量时,可能会因为无法正确表达常量所代表的数值而导致溢出
允许使用并行赋值
变量
var identifier type
当一个变量被声明时,系统自动赋予类型零值
// 类型零值
int 0
float 0.0
bool false
string ""
指针 nil
命名规则遵循骆驼命名法
全局变量 && 局部变量
一般情况下,只有a
和b
的类型相同时,才能进行如a=b
的赋值
声明与赋值(初始化)的语句可以组合起来
使用:=
声明(初始化声明)只能被用于函数体内,即只能用来声明局部变量
// var identifier type = value
var a int = 11
// 编译器自动推断类型(除非自动推断的类型不满足所需)
var a = 11
// 如果从未定义过,可以简写
a := 11
局部变量被声明必须要被使用
全局变量允许声明但不使用
如果想要交换两个变量的值,则可以简单地使用 a, b = b, a
空白标识符 _
也被用于抛弃值(_
实际上是一个只写变量,只是不能得到它的值)
值类型 & 引用类型
所有像 int、float、bool 和 string 这些基本类型都属于值类型,使用这些类型的变量直接指向存在内存中的值
数组和结构体也是值类型
当使用等号 =
将一个变量的值赋值给另一个变量时,如:j = i
,实际上是在内存中将 i 的值进行了拷贝
可以通过 &i
来获取变量 i 的内存地址
值类型的变量的值存储在栈
中
更复杂的数据通常会需要使用多个内存块,这些数据一般使用引用类型保存
一个引用类型的变量 r1 存储的是 r1 的值所在的内存地址(数字),或内存地址中第一个内存块所在的位置,即指针
当使用赋值语句 r2 = r1
时,只有引用(地址)被复制
指针、slice、map、channel 属于引用类型
被引用的变量会存储在堆
中,以便进行垃圾回收,且比栈拥有更大的内存空间
init函数
不能被人为调用
在每个包完成初始化后自动执行,且执行优先级比main
函数高
每个源文件都只能包含一个init
函数。初始化总是以单线程
执行,且按照包的依赖关系顺序执行
也经常被用在当一个程序开始之前调用后台执行的 goroutine
基本类型与运算符
一元运算符只可以用于一个值的操作(作为后缀),而二元运算符则可以和两个值或者操作数结合(作为中缀)
只有两个类型相同的值才可以和二元运算符结合
Go
是强类型语言,不会进行隐式转换(必需显式说明)
布尔 bool
两个类型相同的值可以使用相等 ==
或者不等 !=
运算符来进行比较并获得一个布尔型的值
可以通过和逻辑运算符(非 !
、和 &&
、或 ||
)结合来产生另外一个布尔值
&&
、或 ||
与相等 ==
或不等 !=
属于二元运算符,而非 !
属于一元运算符
建议命名以 is
或者 Is
开头(或类似的)
数字类型
整型 int 和浮点型 float
原生支持复数
与操作系统相关的定义
int
、unint
。32位操作系统:4个字节;64位操作系统:8个字节uintptr
。长度被设定为足够存放一个指针即可
Go
中没有 float 类型,只有float32
、float64
;没有 double 类型
与操作系统架构无关的,从类型名称也可看出来
- 整数:int8 int16 int32 int64
- 无符号整数:uint8 uint16 uint32 uint64
- 浮点:float32 float64
int
型是计算最快的一种类型
整型的零值为 0,浮点型的零值为 0.0
float32
精确到小数点后7
位,float64
精确到小数点后15
位
(使用 == 或 != 比较浮点数时,必需要非常小心
)
尽可能使用float64
(math
包使用这个较多)
增加前缀 0 来表示 8 进制数(如:077),增加前缀 0x 来表示 16 进制数(如:0xFF),使用 e 来表示 10 的连乘
不允许不同类型的变量之间的混合使用(但常量之间可以混合使用)
复数(fixme)
复数使用 re+imI
来表示,其中 re
代表实数部分,im
代表虚数部分,I
代表根号负 1
complex64 (32 位实数和虚数)
complex128 (64 位实数和虚数)
函数 real(c)
和 imag(c)
可以分别获得相应的实数和虚数部分
使用==
或!=
时注意精度
cmath
包含有一些操作复数的公共方法
如果对内存要求不是特别高,最好使用complex128
(已有工具包使用这个比较多)
位运算
只能用于整数类型的变量,且需当它们拥有等长位模式时
二元运算符
- 按位与
&
- 按位或
|
- 按位异或
^
- 位清除
&^
:将指定位置上的值设置为 0
一元运算符
- 按位补足
^
- 位左移
<<
- 位右移
>>
type ByteSize float64
const (
_ = iota // 通过赋值给空白标识符来忽略值
KB ByteSize = 1<<(10*iota)
MB
GB
TB
PB
EB
ZB
YB
)
逻辑运算符
==
、!=
、<
、<=
、>
、>=
==> 因为他们的运算结果总是bool
算术运算符
+
、-
、*
和 /
字符串拼接时允许使用+
的重载,但Go
本身不允许开发者进行自定义的运算符重载
/
对于整数运算而言,结果依旧为整数
取余运算符只能作用于整数
浮点数除以 0.0 会返回一个无穷尽的结果,使用 +Inf
表示
+=
、 -=
、*=
、/=
、%=
整数与浮点数支持自增自减(++
、--
),只有后缀
带有 ++
和 --
的只能作为语句,而非表达式(例:不允许n = i++
这种写法)
在运算时溢出不会产生错误,Go 会简单地将超出位数抛弃
如果需要范围无限大的整数或有理数,可以使用标准库中的big
包
随机数
math/rand
包
rand.Float32
和 rand.Float64
返回介于 [0.0, 1.0) 之间的伪随机数
rand.Intn
返回介于 [0, n) 之间的伪随机数
类型别名
type typeAlias type
类型别名得到的新类型和原类型并非完全相同,新类型不会拥有原类型所附带的方法
字符类型
严格来说,字符并不是Go
的一个类型,字符只是整数的特殊用例
byte
是uint8
的别名;字符使用单引号括起来
// 字符 A 表示
var ch byte = 65
// \x 总是紧跟着长度为 2 的 16 进制数
var ch byte = '\x41'
Go 同样支持 Unicode(UTF-8),因此字符同样称为 Unicode 代码点或者 runes ,并在内存中使用 int 来表示
rune
是 Go 当中的一个类型,并且是 int32
的别名
书写 Unicode 字符时,需要在 16 进制数之前加上前缀 \u
或者 \U
包 unicode
包含了一些针对测试字符的非常有用的函数
- 判断是否为字母:
unicode.IsLetter(ch)
- 判断是否为数字:
unicode.IsDigit(ch)
- 判断是否为空白符号:
unicode.IsSpace(ch)
字符串
是一种值类型,且值不可变
字符串是字节的定长数组
- 解释字符串:转义字符被替换
\n
:换行符\r
:回车符\t
:tab 键\u
或\U
:Unicode 字符\\
:反斜杠自身
- 非解释字符串:反引号,支持换行
`This is a raw string \n` 中的 `\n\` 会被原样输出。
string
类型的零值为长度为零的字符串,即空字符串""
一般的比较运算符(==
、!=
、<
、<=
、>=
、>
)通过在内存中按字节比较
来实现字符串的对比
可通过索引来获取字符串的内容(纯字节),索引从 0 开始(str[index]
;只对纯 ASCII 码的字符串生效)
获取字符串中的某个字节的地址是非法的
使用+
实现拼接
不建议在循环中使用+
进行字符串拼接,推荐strings.Join()
或字节缓冲byts.Buffer
(更好)
strings 和 strconv 包
与字符串相关的类型转换通过 strconv
包实现
任何类型 T 转换为字符串总是成功的
将字符串转换为其它类型 tp 并不总是可能的,可能会在运行时抛出错误
数字类型转换到字符串:
strconv.Itoa(i int) string
返回数字 i 所表示的字符串类型的十进制数strconv.FormatFloat(f float64, fmt byte, prec int, bitSize int) string
将 64 位浮点型的数字转换为字符串fmt
表示格式(其值可以是'b'
、'e
’、'f'
或'g'
)prec
表示精度bitSize
则使用 32 表示 float32,用 64 表示 float64
字符串类型转换为数字类型:
strconv.Atoi(s string) (i int, err error)
将字符串转换为 int 型strconv.ParseFloat(s string, bitSize int) (f float64, err error)
将字符串转换为 float64 型
日期与时间
time
包
一般的格式化设计是通过对于一个标准时间的格式化描述来展现的
fmt.Println(t.Format("02 Jan 2006 15:04"))
// 输出:21 Jul 2011 10:31
fmt.Println(t.Format("2006:01:02 15:04:05"))
// 输出:2021:12:06 09:12:02
time.After
或time.Ticker
实现程序在经过一定时间或周期执行某项任务
指针
指针对于性能非常重要
取地址符是 &
,放到一个变量前使用就会返回相应变量的内存地址
一个指针变量可以指向任何一个值的内存地址 它指向那个值的内存地址,在 32 位机器上占用 4 个字节,在 64 位机器上占用 8 个字节,并且与它所指向的值的大小无关
用一个指针引用一个值被称为间接引用
当一个指针被定义后没有分配到任何变量时,它的值为 nil
。
一个指针变量通常缩写为 ptr
对于任何一个变量 var, 如下表达式都是正确的:var == *(&var)
不能通过&
来获取常量的内存地址
指针也可以指向另一个指针,并且可以进行任意深度的嵌套,导致可以有多级的间接引用,但在大多数情况这会使代码结构不清晰
对一个空指针的反向引用是不合法的,并且会使程序崩溃
func main() {
var p *int = nil
*p = 0
}
格式化输出
%t
用于格式化布尔型
%d
用于格式化整数
%x
和%X
用于格式化 16 进制表示的数字
%g
用于格式化浮点
%f
输出浮点数
%e
输出科学计数表示法
%0d
用于规定输出定长的整数,其中开头的数字 0 必须
%n.mg
用于表示数字 n 并精确到小数点后 m 位,除了使用 g 之外,还可以使用 e 或者 f
%v
来格式化复数,但当你希望只表示其中的一个部分的时候需要使用 %f
%b
用于表示位的格式化标识符
%c
用于格式化字符
%U
输出格式为 U+hhhh 的字符串
%p
用于格式化指针