基本语法
基本语法
Go的基本语法十分简洁且简单,下面通过一个简单的示例来进行讲解。
package main
import "fmt"
func main() {
fmt.Println("Hello 世界!")
}
package
关键字代表的是当前go文件属于哪一个包,启动文件通常是main
包,启动函数是main
函数,在自定义包和函数时命名应当尽量避免与之重复。
import
是导入关键字,后面跟着的是被导入的包名。
func
是函数声明关键字,用于声明一个函数。
fmt.Println("Hello 世界!")
是一个语句,调用了fmt
包下的Println
函数进行控制台输出。
以上就是一个简单的语法介绍。
包
在Go中,程序是通过将包链接在一起来构建的,也可以理解为最基本的调用单位是包,而不是go文件。包其实就是一个文件夹,包内共享所有源文件的变量,常量,函数以及其他类型。包的命名风格建议都是小写字母,并且要尽量简短。
导入
例如创建一个example
包,包下有如下函数
package example
import "fmt"
func SayHello() {
fmt.Println("Hello")
}
在main
函数中调用
package main
import "example"
func main() {
example.SayHello()
}
还可以给包起别名
package main
import e "example"
func main() {
e.SayHello()
}
批量导入时,可以使用括号()
来表示
package main
import (
"fmt"
"math"
)
func main() {
fmt.Println(math.MaxInt64)
}
或者说只导入不调用,通常这么做是为了调用该包下的init
函数。
package main
import (
"fmt"
_ "math" // 下划线表示匿名导入
)
func main() {
fmt.Println(1)
}
注意
在Go中完全禁止循环导入,不管是直接的还是间接的。例如包A导入了包B,包B也导入了包A,这是直接循环导入,包A导入了包C,包C导入了包B,包B又导入了包A,这就是间接的循环导入,存在循环导入的话将会无法通过编译。
导出
在Go中,导出和访问控制是通过命名来进行实现的,如果想要对外暴露一个函数或者一个变量,只需要将其名称首字母大写即可,例如example
包下的SayHello
函数。
package example
import "fmt"
// 首字母大写,可以被包外访问
func SayHello() {
fmt.Println("Hello")
}
如果想要不对外暴露的话,只需将名称首字母改为小写即可,例如下方代码
package example
import "fmt"
// 首字母小写,外界无法访问
func sayHello() {
fmt.Println("Hello")
}
对外暴露的函数和变量可以被包外的调用者导入和访问,如果是不对外暴露的话,那么仅包内的调用者可以访问,外部将无法导入和访问,该规则适用于整个Go语言,例如后续会学到的结构体及其字段,方法,自定义类型,接口等等。
内部包
go中约定,一个包内名为internal
包为内部包,外部包将无法访问内部包中的任何内容,否则的话编译不通过,下面看一个例子。
/home/user/go/
src/
crash/
bang/ (go code in package bang)
b.go
foo/ (go code in package foo)
f.go
bar/ (go code in package bar)
x.go
internal/
baz/ (go code in package baz)
z.go
quux/ (go code in package main)
y.go
由文件结构中可知,crash
包无法访问baz
包中的类型。
注释
Go支持单行注释和多行注释,注释与内容之间建议隔一个空格,例如
// 这是main包
package main
// 导入了fmt包
import "fmt"
/*
*
这是启动函数main函数
*/
func main() {
// 这是一个语句
fmt.Println("Hello 世界!")
}
标识符
标识符就是一个名称,用于包命名,函数命名,变量命名等等,命名规则如下:
- 只能由字母,数字,下划线组成
- 只能以字母和下划线开头
- 严格区分大小写
- 不能与任何已存在的标识符重复,即包内唯一的存在
- 不能与Go任何内置的关键字冲突
下方列出所有的内置关键字,也可以前往参考手册-标识符查看更多细节
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语言中支持的运算符号的优先级排列,也可以前往参考手册-运算符查看更多细节。
Precedence Operator
5 * / % << >> & &^
4 + - | ^
3 == != < <= > >=
2 &&
1 ||
有一点需要稍微注意下,go语言中没有选择将~
作为取反运算符,而是复用了^
符号,当两个数字使用^
时,例如a^b
,它就是异或运算符,只对一个数字使用时,例如^a
,那么它就是取反运算符。go也支持增强赋值运算符,如下。
a += 1
a /= 2
a &^= 2
提示
Go语言中没有自增与自减运算符,它们被降级为了语句statement
,并且规定了只能位于操作数的后方,所以不用再去纠结i++
和++i
这样的问题。
a++ // 正确
++a // 错误
a-- // 正确
还有一点就是,它们不再具有返回值,因此a = b++
这类语句的写法是错误的。
字面量
字面量,按照计算机科学的术语来讲是用于表达源代码中一个固定值的符号,也叫字面值。两个叫法都是一个意思,写了什么东西,值就是什么,值就是“字面意义上“的值。
整型字面量
为了便于阅读,允许使用下划线_
来进行数字划分,但是仅允许在前缀符号之后和数字之间使用。
24 // 24
024 // 24
2_4 // 24
0_2_4 // 24
10_000 // 10k
100_000 // 100k
0O24 // 20
0b00 // 0
0x00 // 0
0x0_0 // 0
浮点数字面量
通过不同的前缀可以表达不同进制的浮点数
0.
72.40
072.40 // == 72.40
2.71828
1.e+0
6.67428e-11
1E6
.25
.12345E+5
1_5. // == 15.0
0.15e+0_2 // == 15.0
0x1p-2 // == 0.25
0x2.p10 // == 2048.0
0x1.Fp+0 // == 1.9375
0X.8p-0 // == 0.5
0X_1FFFP-16 // == 0.1249847412109375
0x15e-2 // == 0x15e - 2 (integer subtraction)
复数字面量
0i
0123i // == 123i
0o123i // == 0o123 * 1i == 83i
0xabci // == 0xabc * 1i == 2748i
0.i
2.71828i
1.e+0i
6.67428e-11i
1E6i
.25i
.12345E+5i
0x1p-2i // == 0x1p-2 * 1i == 0.25i
字符字面量
字符字面量必须使用单引号括起来''
,Go中的字符完全兼容utf8
。
'a'
'ä'
'你'
'\t'
'\000'
'\007'
'\377'
'\x07'
'\xff'
'\u12e4'
'\U00101234'
转义字符
Go中可用的转义字符
\a U+0007 响铃符号(建议调高音量)
\b U+0008 回退符号
\f U+000C 换页符号
\n U+000A 换行符号
\r U+000D 回车符号
\t U+0009 横向制表符号
\v U+000B 纵向制表符号
\\ U+005C 反斜杠转义
\' U+0027 单引号转义 (该转义仅在字符内有效)
\" U+0022 双引号转义 (该转义仅在字符串内有效)
字符串字面量
字符串字面量必须使用双引号""
括起来或者反引号(反引号字符串不允许转义)
`abc` // "abc"
`\n
\n` // "\\n\n\\n"
"\n"
"\"" // `"`
"Hello, world!\n"
"今天天气不错"
"日本語"
"\u65e5本\U00008a9e"
"\xff\u00FF"
风格
关于编码风格这一块Go是强制所有人统一同一种风格,Go官方提供了一个格式化工具gofmt
,通过命令行就可以使用,该格式化工具没有任何的格式化参数可以传递,仅有的两个参数也只是输出格式化过程,所以完全不支持自定义,也就是说所有通过此工具的格式化后的代码都是同一种代码风格,这会极大的降低维护人员的心智负担,所以在这一块追求个性显然是一个不太明智的选择。
下面会简单列举一些规则,平时在编写代码的时候也可以稍微注意一下。
- 花括号,关于花括号
{}
到底该不该换行,几乎每个程序员都能说出属于自己的理由,在Go中所有的花括号都不应该换行。
// 正确示例
func main() {
fmt.Println("Hello 世界!")
}
// 错误示例
func main()
{
fmt.Println("Hello 世界!")
}
缩进,Go默认使用
tabs
也就是制表符进行缩进,仅在一些特殊情况会使用空格。间隔,Go中大部分间隔都是有意义的,从某种程度上来说,这也代表了编译器是如何看待你的代码的,例如下方的数学运算
2*9 + 1/3*2
众所周知,乘法的优先级比加法要高,在格式化后,
*
符号之间的间隔会显得更紧凑,意味着优先进行运算,而+
符号附近的间隔则较大,代表着较后进行运算。还是花括号,花括号在任何时候都不能够省略,就算是只有一行代码,例如
// 正确示例 if a > b { a++ } // 错误示例 if a > b a++