本文最后更新于 2025年1月20日 凌晨
GoGoGo!
先看一个简单的代码,熟悉一下简单语法
1 2 3 4 5 6 7
| package main
import "fmt"
func main() { fmt.Println("Hello butt3rf1y!") }
|
package:声明当前文件属于哪一个包,入口文件都必须为 main
包,入口函数是 main 函数。
import:导入关键字,fmt 是导入的包名。
func:函数声明关键字。
包
在 go 中,程序通过将包链接在一起来构建,go 中导入的最基本单位是一个包,不是 .go
文件。包内共享所有变量,常量以及所有定义的类型,而且包名建议小写。
可见性
go 中控制包内资源可见性有两种方法
- 名称大写字母开头,即为公有
- 小写或下划线开头,即为私有
1 2 3 4 5
| package butt3r
const User = "butt3rf1y"
const passwd = 114514
|
导入
导入包就是导入这个包的所有公有的类型/变量/常量
导入单个包时
1 2 3
| package main
import "example"
|
导入多个包时
1 2 3 4
| package main
import "example" import "example2"
|
导入多个包时也可以用 {}
括起来
1 2 3 4 5 6
| package main
import ( "example" "example1" )
|
如果包名重复或比较复杂,可以起一个别名
1 2 3 4 5 6
| package main
import ( e "example" e1 "example1"
|
还有一种匿名导入包,但是匿名导入的包无法使用,一般这么做是为了加载包下的 init
函数,但又不需要包中的类型,比如说注册数据库驱动
1 2 3 4 5 6
| package main
import ( e "example" _ "mysql-driver" )
|
导入包后,可以通过 名称.标识符
去访问,比如
1 2 3 4 5 6 7 8 9 10
| package main
import ( "example" "fmt" )
func main() { fmt.Println(example.User) }
|
私有类型无法访问,并且 go 不支持循环导入,不管是直接的还是间接的。例如包 A 导入了包 B,包 B 也导入了包 A,这是直接循环导入;包 A 导入了包 C,包 C 导入了包 B,包 B 又导入了包 A,这就是间接的循环导入,存在循环导入的话将会无法通过编译。
内部包
go 中一个包内名为 internal
包为内部包,外部包无法访问内部包
标识符
go 中所有内置关键字
1 2 3 4 5
| 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 语言中支持的运算符号的优先级排列
1 2 3 4 5 6
| Precedence Operator 5 * / % << >> & &^ 4 + - | ^ 3 == != < <= > >= 2 && 1 ||
|
go 语言中没有选择将~
作为取反运算符,而是复用了^
符号,当两个数字使用^
时,例如a^b
,它就是异或运算符,只对一个数字使用时,例如^a
,那么它就是取反运算符。go 也支持增强赋值运算符
注意:go 中没有自增和自减,他们变成了语句 statement
,并且规定了只能位于操作数的后方,所以不用再去纠结 i++
和++i
这种不太聪明的问题,但是没有返回值。甚至 a = b++
这种写法是错误的。
字面量
整型字面量
允许使用 _
进行数字划分,但是仅仅允许在前缀符号之后和数字之间使用
1 2 3 4 5 6 7 8 9 10
| 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
|
浮点型字面量
通过不同前缀可以表达不同进制浮点数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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)
|
复数字面量
1 2 3 4 5 6 7 8 9 10 11 12
| 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
|
字符字面量
字符字面量必须使用单引号括起来 ''
1 2 3 4 5 6 7 8 9
| 'a' 'ä' '你' '\t' '\007' '\x07' '\xff' '\u12e4' '\U00101234'
|
转义字符
1 2 3 4 5 6 7 8 9 10
| \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 双引号转义 (该转义仅在字符串内有效)
|
字符串字面量
字符串字面量必须使用双引号 ""
括起来或者反引号(反引号字符串不允许转义)
1 2 3 4 5 6 7 8 9
| `abc` `\n \n` "\n" "\"" "kfcvme50!\n" "疯狂星期四v我50" "\u65e5本\U00008a9e" "\xff\u00FF"
|
函数
通过 func
关键字来进行
1 2 3
| func main() { fmt.Println(1) }
|
参数类型后置
1 2 3
| func Hello(name string) { fmt.Println(name) }
|
多返回值
1 2 3
| func Pos() () (x, y float64) { ... }
|
代码风格
花括号换行
go 中花括号都不能换行
1 2 3 4 5 6 7 8 9 10
| func main() { fmt.Println("Hello butt3rf1y!") }
func main() { fmt.Println("Hello butt3rf1y!") }
|
代码缩进
go 中代码间隔也有一定的意义,比如说
在格式化后,*
符号之间的间隔会显得更紧凑,意味着优先进行运算,而 +
符号附近的间隔则较大,代表着较后进行运算。
省略
在 go 中的 if 和 for 语句可以只写一行,但必须加上花括号
1 2 3 4 5
| func main() { for i := 0; i < 10; i++ { fmt.Println(i) } }
|
三元表达式
go 中没有三元表达式,不能写像 c 语言中的 temp = (a > b) ? a : b;
这种表达式。