文章目录
一、Go环境准备
环境配置
要使用go首先需要配置几个环境变量:
-
GOROOT
:go语言所在的目录,用于全局执行go相关的命令
path中也需要配置
-
GOPATH
:工作目录,工程代码存放的位置,此目录下,一个文件夹就是一个工程 -
GOPROXY
:代理,需要配置代理,有好心人做了代理
地址:https://goproxy.io/zh/ 可以去看文档
-
go env 可以检查环境变量的配置是否正确
初始化项目
在我们的GOPATH路径下,新建一个文件夹,hello
进入hello目录,运行cmd命令
# 运行下面的命令
go mod init hello
执行后,会生成一个go.mod文件,如果学过maven的话,可以将其当成maven的pom.xml,用于管理依赖的。
二、变量&常量
基本类型
计算机中数据存储的最小单位为bit(位),0或者1
byte:计算机中数据的基本单元,1字节=8bit,数据在计算机中存储或者计算,至少为1个字节
- bool
- string
- int(随系统,一般是占用4个字节)、int8(占一个字节)、int16(占两个字节)、int32(占4个字节)、int64(占8个字节)
- uint(无符号整数)、uint8、uint16、uint32、uint64、uintptr
- byte //
uint8 的别名
- rune //
int32 的别名 代表一个 Unicode 码
- float32、float64
- complex64、complex128
有符号和无符号的区别:int8 范围 -128-127,uint8 范围:0-255
当一个变量被声明之后,系统自动赋予它该类型的零值:
int 为 0
,float 为 0.0
,bool 为 false
,string 为空字符串
,指针为 nil
简短变量
我们可以省略
var
关键字,这样写起来更加便捷
//i是变量名 1 是值(或者表达式)
i := 1
上面讲过,如果不指明类型,直接赋值,Go会自动推导类型
使用简短格式有以下限制:
- 定义变量,同时显式初始化
- 不能提供数据类型
- 只能用在函数内部
package main
import "fmt"
//不能
//aa :=1
func main() {
aa :=1
fmt.Println(aa)
}
简短变量声明被广泛用于大部分的局部变量的声明和初始化,var 形式的声明语句往往用于需要显式指定变量类型的地方
匿名变量
使用多重赋值
时,如果不需要在左值中接受变量
,可以使用匿名变量
比如上面的例子:
package main
import (
"fmt"
"net"
)
func main() {
//conn, err := net.Dial("tcp", "127.0.0.1:8080")
//如果不想接收err的值,那么可以使用_表示,这就是匿名变量
conn, _ := net.Dial("tcp", "127.0.0.1:8080")
fmt.Println(conn)
}
匿名变量以“_”下划线表示
匿名变量不占用命名空间,也不会分配内存。匿名变量可以重复声明使用
“_”本身就是一个特殊的标识符,被称为空白标识符。它可以像其他标识符那样用于变量的声明或赋值(任何类型都可以赋值给它),但任何赋给这个标识符的值都将被抛弃,因此这些值不能在后续的代码中使用,也不可以使用这个标识符作为变量对其它变量进行赋值或运算。
作用域
Go语言(静态语言)会在编译时检查每个变量是否使用过,一旦出现未使用的变量,就会报编译错误
。
如果不能理解变量的作用域,就有可能会带来一些不明所以的编译错误。
根据变量定义位置的不同,可以分为以下三个类型:
- 函数内定义的变量称为局部变量
- 函数外定义的变量称为全局变量
- 函数定义中的变量称为形式参数
在函数体内声明的变量称之为局部变量
,它们的作用域只在函数体内
,函数的参数和返回值变量都属于局部变量。
局部变量
局部变量不是一直存在的,它只在定义它的函数被调用后存在,函数调用结束后这个局部变量就会被销毁。
package main
import (
"fmt"
)
func main() {
//声明局部变量 a 和 b 并赋值
var a int = 3
var b int = 4
//声明局部变量 c 并计算 a 和 b 的和
c := a + b
fmt.Printf("a = %d, b = %d, c = %d\n", a, b, c)
}
全局变量
在函数体外声明的变量称之为全局变量
,全局变量只需要在一个源文件中定义,就可以在所有源文件中使用
,当然,不包含这个全局变量的源文件需要使用“import”关键字引入全局变量所在的源文件之后才能使用这个全局变量。
全局变量声明必须以 var 关键字开头
,如果想要在外部包中使用全局变量的首字母必须大写
。
package main
import "fmt"
//声明全局变量
var c int
func main() {
//声明局部变量
var a, b int
//初始化参数
a = 3
b = 4
c = a + b
fmt.Printf("a = %d, b = %d, c = %d\n", a, b, c)
}
Go语言程序中全局变量与局部变量名称可以相同,但是函数体内的局部变量会被优先考虑。
package main
import "fmt"
//声明全局变量
var bb float32 = 3.14
func main() {
bb := 3
fmt.Println(bb)
}
//执行结果 3
形式参数
在定义函数时函数名后面括号中的变量叫做形式参数
(简称形参)。形式参数只在函数调用时才会生效,函数调用结束后就会被销毁,在函数未被调用时,函数的形参并不占用实际的存储单元
,也没有实际值。
形式参数会作为函数的局部变量来使用
。
package main
import (
"fmt"
)
//全局变量 a
var a int = 13
func main() {
//局部变量 a 和 b
var a int = 3
var b int = 4
fmt.Printf("main() 函数中 a = %d\n", a)
fmt.Printf("main() 函数中 b = %d\n", b)
c := sum(a, b)
fmt.Printf("main() 函数中 c = %d\n", c)
}
func sum(a, b int) int {
fmt.Printf("sum() 函数中 a = %d\n", a)
fmt.Printf("sum() 函数中 b = %d\n", b)
num := a + b
return num
}
变量的生命周期
变量的生命周期指的是在程序运行期间变量有效存在的时间间隔。
变量的生命周期与变量的作用域有不可分割的联系:
- 全局变量:它的生命周期和整个程序的运行周期是一致的;
- 局部变量:它的生命周期则是动态的,从创建这个变量的声明语句开始,到这个变量不再被引用为止;
- 形式参数和函数返回值:它们都属于局部变量,在函数被调用的时候创建,函数调用结束后被销毁。
go的内存中应用了两种数据结构用于存放变量:
- 堆(heap):堆是用于存放进程执行中被动态分配的内存段。它的大小并不固定,可动态扩张或缩减。当进程调用 malloc 等函数分配内存时,新分配的内存就被动态加入到堆上(堆被扩张)。当利用 free 等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减);
- 栈(stack):栈又称堆栈, 用来存放程序暂时创建的局部变量,也就是我们函数的大括号
{ }
中定义的局部变量。
栈是先进后出,往栈中放元素的过程,称为入栈,取元素的过程称为出栈。
栈可用于内存分配,栈的分配和回收速度非常快
在程序的编译阶段,编译器会根据实际情况自动选择
在栈
或者堆
上分配局部变量的存储空间,不论使用 var 还是 new 关键字声明变量都不会影响编译器的选择。
var global *int
func f() {
var x int
x = 1
global = &x
}
func g() {
y := new(int)
*y = 1
}
上述代码中,函数 f 里的变量 x 必须在堆上分配,因为它在函数退出后依然可以通过包一级的 global 变量找到,虽然它是在函数内部定义的。
用Go语言的术语说,这个局部变量 x 从函数 f 中逃逸了。
常量
Go语言中的常量使用关键字const
定义,用于存储不会改变的数据,常量是在编译时被创建的,即使定义在函数内部也是如此,并且只能是布尔型
、数字型
(整数型、浮点型和复数)和字符串型
。
由于编译时的限制,定义常量的表达式必须为能被编译器求值的常量表达式。
声明格式:
const name [type] = value
例如:
const pi = 3.14159
type可以省略
和变量声明一样,可以批量声明多个常量:
const (
e = 2.7182818
pi = 3.1415926
)
所有常量的运算都可以在编译期完成,这样不仅可以减少运行时的工作,也方便其他代码的编译优化,当操作数是常量时,一些运行时的错误也可以在编译时被发现,例如整数除零、字符串索引越界、任何导致无效浮点数的操作等。
常量间的所有算术运算、逻辑运算和比较运算的结果也是常量,对常量的类型转换操作或以下函数调用都是返回常量结果:len、cap、real、imag、complex 和 unsafe.Sizeof。
因为它们的值是在编译期就确定的,因此常量可以是构成类型的一部分
如果是批量声明的常量,除了第一个外其它的常量右边的初始化表达式都可以省略,如果省略初始化表达式则表示使用前面常量的初始化表达式,对应的常量类型也是一样的。例如:
const (
a = 1
b
c = 2
d
)
fmt.Println(a, b, c, d) // "1 1 2 2"
iota 常量生成器
常量声明可以使用 iota 常量生成器初始化,它用于生成一组以相似规则初始化的常量,但是不用每行都写一遍初始化表达式。
在一个 const 声明语句中,在第一个声明的常量所在的行,iota 将会被置为 0,然后在每一个有常量声明的行加1
比如,定义星期日到星期六,从0-6
const (
Sunday = iota //0
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday //6
)
三、基本类型
整型
Go语言同时提供了有符号和无符号的整数类型。
- 有符号整型:int、int8、int64、int32、int64
- 无符号整型:uint、uint8、uint64、uint32、uint64、uintptr
有符号整型范围:
-2^(n-1) 到 2^(n-1)-1
无符号整型范围:
0 到 2^n-1
实际开发中由于编译器和计算机硬件的不同,int 和 uint 所能表示的整数大小会在 32bit 或 64bit 之间变化。
uint在硬件开发中使用
用来表示 Unicode 字符的 rune 类型
和 int32 类型
是等价的,通常用于表示一个 Unicode 码点
。这两个名称可以互换使用。同样,byte
和 uint8
也是等价类型,byte 类型一般用于强调数值是一个原始的数据
而不是一个小的整数。
无符号的整数类型
uintptr
,它没有指定具体的 bit 大小但是足以容纳指针。uintptr 类型只有在底层编程
时才需要,特别是Go语言和C语言函数库或操作系统接口相交互的地方。
在二进制传输、读写文件的结构描述时,为了保持文件的结构不会受到不同编译目标平台字节长度的影响,不要使用 int 和 uint
浮点型
Go语言支持两种浮点型数:
float32
: 范围 约1.4e-45 到 约3.4e38float64
:范围约4.9e-324 到 约1.8e308
floatStr1 := 3.2
//保留小数点位数
fmt.Printf("%.2f\n", floatStr1)
算术规范由 IEEE754 浮点数国际标准定义,该浮点数规范被所有现代的 CPU 支持
通常应该优先使用 float64 类型,因为 float32 类型的累计计算误差很容易扩散,并且 float32 能精确表示的正整数并不是很大。
var f float32 = 1 << 24;
fmt.Println(f == f+1) // true
浮点数在声明的时候可以只写整数部分或者小数部分
var e = .71828 // 0.71828
var f = 1. // 1
fmt.Printf("%.5f,%.1f",e,f)
很小或很大的数最好用科学计数法书写,通过 e 或 E 来指定指数部分
var avogadro = 6.02214129e23 // 阿伏伽德罗常数
var planck = 6.62606957e-34 // 普朗克常数
fmt.Printf("%f,%.35f",avogadro,planck)
布尔型
在Go语言中,以bool类型进行声明:
var 变量名 bool
==
,>
,<
,<=
, >=
,&&(AND)
,||(OR)
等都会产生bool值
var aVar = 10
aVar == 5 // false
aVar == 10 // true
aVar != 5 // true
aVar != 10 // false
Go语言对于值之间的比较有非常严格的限制,只有两个相同类型的值才可以进行比较,如果值的类型是接口(interface),那么它们也必须都实现了相同的接口。
如果其中一个值是
常量
,那么另外一个值可以不是常量,但是类型必须和该常量类型相同。如果以上条件都不满足,则必须将其中一个值的类型转换为和另外一个值的类型相同之后才可以进行比较。
&&(AND)
,||(OR)
是具有短路行为的,如果运算符左边的值已经可以确定整个布尔表达式的值,那么运算符右边的值将不再被求值。(&&优先级高于||)
var a = 10
//因为a>11已经不满足了,所以a < 30不会走,整个表达式为false
if(a > 11 && a < 30){
fmt.Println("正确")
}else{
fmt.Println("错误")
}
//因为a > 5已经满足了,所以a < 30不会走,整个表达式为true
if(a > 5 || a < 30){
fmt.Println("正确")
}else{
fmt.Println("错误")
}
布尔型数据只有true和false,且不能参与任何计算以及类型转换
字符类型
Go语言的字符有以下两种:
- 一种是 uint8 类型,或者叫 byte 型,代表了 ASCII 码的一个字符。
- 另一种是 rune 类型,代表一个 UTF-8 字符,当需要处理中文、日文或者其他复合字符时,则需要用到 rune 类型。rune 类型等价于 int32 类型。
byte 类型是 uint8 的别名,rune 类型是int32的别名
ASCII 码的一个字符占一个字节
ASCII 定义 128 个字符,由码位 0 – 127 标识。它涵盖英文字母,拉丁数字和其他一些字符。
Unicode 是 ASCII 的超集,它定义了 1,114,112 个代码点的代码空间。 Unicode 版本 10.0 涵盖 139 个现代和历史文本集(包括符文字母,但不包括 Klingon )以及多个符号集。
Go语言同样支持 Unicode(UTF-8), 用rune来表示
, 在内存中使用 int 来表示。
在书写 Unicode 字符时,需要在 16 进制数之前加上前缀\u
或者\U
。如果需要使用到 4 字节,则使用\u
前缀,如果需要使用到 8 个字节,则使用\U
前缀。
var ch rune = '\u0041'
var ch1 int64 = '\U00000041'
//格式化说明符%c用于表示字符,%v或%d会输出用于表示该字符的整数,%U输出格式为 U+hhhh 的字符串。
fmt.Printf("%c,%c,%U",ch,ch1,ch)
Unicode 包中内置了一些用于测试字符的函数,这些函数的返回值都是一个布尔值,如下所示(其中 ch 代表字符):
- 判断是否为字母:unicode.IsLetter(ch)
- 判断是否为数字:unicode.IsDigit(ch)
- 判断是否为空白符号:unicode.IsSpace(ch)
字符串类型
一个字符串是一个不可改变的字节序列,字符串可以包含任意的数据,但是通常是用来包含可读的文本,字符串是 UTF-8 字符的一个序列。
go语言从底层就支持UTF-8编码。
UTF-8 是一种被广泛使用的编码格式,是文本文件的标准编码。
由于该编码对占用字节长度的不定性,在Go语言中字符串也可能根据需要占用 1 至 4 个字节,这与其它编程语言不同。
Go语言这样做不仅减少了内存和硬盘空间占用,同时也不用像其它语言那样需要对使用 UTF-8 字符集的文本进行编码和解码。
字符串是一种值类型,且值不可变,即创建某个文本后将无法再次修改这个文本的内容。
当字符为 ASCII 码表上的字符时则占用 1 个字节
字符串拼接符“+”
两个字符串 s1 和 s2 可以通过 s := s1 + s2 拼接在一起。将 s2 追加到 s1 尾部并生成一个新的字符串 s。
//因为编译器会在行尾自动补全分号,所以拼接字符串用的加号“+”必须放在第一行末尾。
str := "第一部分 " +
"第二部分"
也可以使用“+=”来对字符串进行拼接:
s := "hel" + "lo,"
s += "world!"
fmt.Println(s) //输出 “hello, world!”
字符串的格式化
-
print :
结果写到标准输出 -
Sprint:
结果会以字符串形式返回 -
Sprint:
结果会以字符串形式返回
str1 := "你好,"
str2 := "hello fsq"
var stringBuilder bytes.Buffer
stringBuilder.WriteString(str1)
stringBuilder.WriteString(str2)
// Sprint 以字符串形式返回
result := fmt.Sprintf(stringBuilder.String())
fmt.Println(result)
%c 单一字符
%T 动态类型
%v 本来值的输出
%+v 字段名+值打印
%d 十进制打印数字
%p 指针,十六进制
%f 浮点数
%b 二进制
%s string
字符串查找
如何获取字符串中的某一段字符?
- strings.Index(): 正向搜索子字符串
- strings.LastIndex():反向搜索子字符串
package main
import (
"fmt"
"strings"
)
func main() {
// 查找
tracer := "张三来了,张三bye bye"
// 正向搜索字符串
comma := strings.Index(tracer, ",")
fmt.Println(",所在的位置:",comma)
fmt.Println(tracer[comma+1:]) // 张三bye bye
add := strings.Index(tracer, "+")
fmt.Println("+所在的位置:",add) // +所在的位置: -1
pos := strings.Index(tracer[comma:], "张三")
fmt.Println("张三,所在的位置", pos) // 张三,所在的位置 1
fmt.Println(comma, pos, tracer[5+pos:]) // 12 1 张三bye bye
}
类型转换
在必要以及可行的情况下,一个类型的值可以被转换成另一种类型的值。由于Go语言不存在隐式类型转换,因此所有的类型转换都必须显式的声明:
//类型 B 的值 = 类型 B(类型 A 的值)
valueOfTypeB = type B(valueOfTypeA)
示例:
a := 5.0
b := int(a)
类型转换只能在定义正确的情况下转换成功,例如从一个取值范围较小的类型转换到一个取值范围较大的类型(将 int16 转换为 int32)。
当从一个取值范围较大的类型转换到取值范围较小的类型时(将 int32 转换为 int16 或将 float32 转换为 int),会发生精度丢失
的情况。
只有相同底层类型的变量之间可以进行相互转换(如将 int16 类型转换成 int32 类型),不同底层类型的变量相互转换时会引发编译错误(如将 bool 类型转换为 int 类型)
修改字符串
修改字符串时,可以将字符串转换为[]byte
进行修改
[]byte和string可以通过强制类型转换
案例:将8080改为8081
package main
import "fmt"
func main() {
s1 := "localhost:8080"
fmt.Println(s1)
// 强制类型转换 string to byte
strByte := []byte(s1)
// 下标修改
strByte[len(s1)-1] = '1'
fmt.Println(strByte)
// 强制类型转换 []byte to string
s2 := string(strByte)
fmt.Println(s2)
}
字符串与其他数据类型的转换
-
整数 与 字符串
// 字符串与其他类型的转换 // str 转 int newStr1 := "1" intValue, _ := strconv.Atoi(newStr1) fmt.Printf("%T,%d\n", intValue, intValue) // int,1 // int 转 str intValue2 := 1 strValue := strconv.Itoa(intValue2) fmt.Printf("%T, %s\n", strValue, strValue)
-
浮点数 与字符串
// str 转 float string3 := "3.1415926" f,_ := strconv.ParseFloat(string3, 32) fmt.Printf("%T, %f\n", f, f) // float64, 3.141593 //float 转 string floatValue := 3.1415926 //4个参数,1:要转换的浮点数 2. 格式标记(b、e、E、f、g、G) //3. 精度 4. 指定浮点类型(32:float32、64:float64) // 格式标记: // ‘b’ (-ddddp±ddd,二进制指数) // ‘e’ (-d.dddde±dd,十进制指数) // ‘E’ (-d.ddddE±dd,十进制指数) // ‘f’ (-ddd.dddd,没有指数) // ‘g’ (‘e’:大指数,‘f’:其它情况) // ‘G’ (‘E’:大指数,‘f’:其它情况) // // 如果格式标记为 ‘e’,‘E’和’f’,则 prec 表示小数点后的数字位数 // 如果格式标记为 ‘g’,‘G’,则 prec 表示总的数字位数(整数部分+小数部分) formatFloat := strconv.FormatFloat(floatValue, 'f', 2, 64) fmt.Printf("%T,%s",formatFloat,formatFloat)
四、指针
指针(pointer)在Go语言中可以被拆分为两个核心概念:
- 类型指针,允许对这个指针类型的数据进行修改,传递数据可以直接使用指针,而无须拷贝数据,类型指针不能进行偏移和运算。
- 切片,由指向起始元素的原始指针、元素数量和容量组成。
一个指针变量可以指向任何一个值的内存地址,它所指向的值的内存地址在 32 和 64 位机器上分别占用 4 或 8 个字节,占用字节的大小与所指向的值的大小无关。
当一个指针被定义后没有分配到任何变量
时,它的默认值为 nil
。
每个变量在运行时都拥有一个地址,这个地址代表变量在内存中的位置。
Go语言中使用在变量名前面添加&
操作符(前缀)来获取变量的内存地址(取地址操作),格式如下:
//其中 v 代表被取地址的变量,变量 v 的地址使用变量 ptr 进行接收,ptr 的类型为*T,称做 T 的指针类型,*代表指针。
ptr := &v // v 的类型为 T
package main
import (
"fmt"
)
func main() {
var cat int = 1
var str string = "ispainting"
fmt.Printf("%p %p", &cat, &str)
}
变量、指针和地址三者的关系是,每个变量都拥有地址,指针的值就是地址
当使用
&
操作符对普通变量进行取地址操作并得到变量的指针后,可以对指针使用*
操作符,也就是指针取值
取地址操作符&
和取值操作符*
是一对互补操作符,&
取出地址,*
根据地址取出地址指向的值
变量、指针地址、指针变量、取地址、取值的相互关系和特性如下:
- 对变量进行取地址操作使用
&
操作符,可以获得这个变量的指针变量。 - 指针变量的值是指针地址。
- 对指针变量进行取值操作使用
*
操作符,可以获得指针变量指向的原变量的值。
Go语言内置的 flag 包实现了对命令行参数的解析,flag 包使得开发命令行工具更为简单。
package main
// 导入系统包
import (
"flag"
"fmt"
)
// 定义命令行参数
var mode = flag.String("mode", "", "fast模式能让程序运行的更快")
func main() {
// 解析命令行参数
flag.Parse()
fmt.Println(*mode)
}
五、关键字和标识符
关键字
关键字即是被Go语言赋予了特殊含义的单词,也可以称为保留字。
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语言中的关键字保持的这么少,是为了简化在编译过程中的代码解析。
和其它语言一样,关键字不能够作标识符使用。
标识符
标识符是指Go语言对各种变量、方法、函数等命名时使用的字符序列,标识符由若干个字母、下划线_
、和数字组成,且第一个字符必须是字母。
下划线_
是一个特殊的标识符,称为空白标识符
标识符的命名需要遵守以下规则:
- 由 26 个英文字母、0~9、
_
组成; - 不能以数字开头,例如
var 1num int
是错误的; - Go语言中严格区分大小写;
- 标识符不能包含空格;
- 不能以系统保留关键字作为标识符,比如 break,if 等等。
命名标识符时还需要注意以下几点:
- 标识符的命名要尽量采取简短且有意义;
- 不能和标准库中的包名重复;
- 为变量、函数、常量命名时采用驼峰命名法,例如 stuName、getVal;
在Go语言中还存在着一些特殊的标识符,叫做预定义标识符,如下表所示:
append | bool | byte | cap | close | complex | complex64 | complex128 | uint16 |
---|---|---|---|---|---|---|---|---|
copy | false | float32 | float64 | imag | int | int8 | int16 | uint32 |
int32 | int64 | iota | len | make | new | nil | panic | uint64 |
println | real | recover | string | true | uint | uint8 | uintptr |
预定义标识符一共有 36 个,主要包含Go语言中的基础数据类型和内置函数,这些预定义标识符也不可以当做标识符来使用。
运算符优先级
Go语言有几十种运算符,被分成十几个级别,有的运算符优先级不同,有的运算符优先级相同,请看下表。
优先级 | 分类 | 运算符 | 结合性 |
---|---|---|---|
1 | 逗号运算符 | , | 从左到右 |
2 | 赋值运算符 | =、+=、-=、*=、/=、 %=、 >=、 <<=、&=、^=、|= | 从右到左 |
3 | 逻辑或 | || | 从左到右 |
4 | 逻辑与 | && | 从左到右 |
5 | 按位或 | | | 从左到右 |
6 | 按位异或 | ^ | 从左到右 |
7 | 按位与 | & | 从左到右 |
8 | 相等/不等 | ==、!= | 从左到右 |
9 | 关系运算符 | <、<=、>、>= | 从左到右 |
10 | 位移运算符 | <<、>> | 从左到右 |
11 | 加法/减法 | +、- | 从左到右 |
12 | 乘法/除法/取余 | *(乘号)、/、% | 从左到右 |
13 | 单目运算符 | !、*(指针)、& 、++、–、+(正号)、-(负号) | 从右到左 |
14 | 后缀运算符 | ( )、[ ]、-> | 从左到右 |
注意:优先级值越大,表示优先级越高。
一下子记住所有运算符的优先级并不容易,还好Go语言中大部分运算符的优先级和数学中是一样的,大家在以后的编程过程中也会逐渐熟悉起来。如果实在搞不清,可以加括号,就像下面这样:
d := a + (b * c)
括号的优先级是最高的,括号中的表达式会优先执行,这样各个运算符的执行顺序就一目了然了。