Golang 基础知识
本文整合了 Golang 的基础知识体系,包括基础语法规范、包管理(
package与import) 以及特有的指针概念,适合 Go 语言初学者参考。
1. 简介
GO 语言特点
- 运行效率高,开发高效,部署简单 (可直接编译成机器码,不依赖其他库,glibc的版本有一定要求,部署就是扔一个文件上去就完成了)
- 语言层面支持并发,易于利用多核实现并发 (最大特点)
- 内置
runtime(作用:性能监控,GC等),支持垃圾回收 - 丰富标准库,强大网络库
- 内置强大的工具(
gofmt),跨平台编译,内嵌C支持
GO 语言的应用
- 服务器编程,以前你如果使用
C或者C++做的那些事情,用Go来做很合适,例如处理日志、数据打包、虚拟机处理、文件系统等。 - 分布式系统,数据库代理器等
- 网络编程,这一块目前应用最广,包括Web应用、API应用、下载应用、
- 内存数据库,前一段时间
google开发的groupcache,couchbase的部分组建 - 云平台,目前国外很多云平台在采用
Go开发,CloudFoundy的部分组建,前VMare的技术总监自己出来搞的apcera云平台。
GO 语言命令行工具
go build用于编译源码文件,代码包,依赖包;go run可以编译并运行Go源码文件go get主要用来动态获取远程代码包
编译 windows x86_64 GOOS=windows GOARCH=amd64 go build hello.go
GO 安装
使用 linux
1 | cd /usr/src |
一些配置
1 | go env -w GOPROXY=https://goproxy.cn,https://mirrors.aliyun.com/goproxy/,direct |
2. 基础语法
一般结构
1 | // 当前程序的包名 |
变量
1 | package main |
Print / Println / println 三者的差别
上面那两行输出看着自相矛盾,其实是三条不同的规则:
fmt.Print:仅当相邻两个操作数都不是字符串时才补空格。
1 | fmt.Print(1, 2) // "1 2" 相邻两者都不是 string → 补空格 |
所以 fmt.Print(b, c) 打印 1 2(都是 int),而两次 fmt.Print("hello") / ("world") 是两次独立调用,之间当然也没有空格 —— 得到 helloworld。这条规则很反直觉,是 Print 系列最容易踩的地方。
fmt.Println:无条件在每个操作数之间补空格,末尾加换行。 不管是什么类型。所以想要稳定的分隔就用 Println,或者干脆 Printf 自己写格式。
内置的 println(不带 fmt.)是另一回事。 它是编译器内置函数,不需要 import:
- 输出到 stderr 而不是 stdout —— 所以
go run x.go > out.txt时它的内容不会进文件; - 语言规范明确说它的行为不保证,编译器可以自由实现,也可能在未来被移除;
- 不支持任何格式化动词。
它只适合临时插桩调试,正式代码里不该出现。
别把变量当格式串
1 | fmt.Printf(vname1, vname2, vname3) // 不要这样写 |
Printf 的第一个参数是格式串。传一个普通变量进去,里面没有任何 % 动词,后面两个参数就无处安放,fmt 会在输出末尾追加一个错误标记:
1 | v1%!(EXTRA string=v2, string=v3) |
go vet 会把这种写法报成 printf: non-constant format string in call to fmt.Printf(Go 1.24 起对此更严格,因为格式串来自变量时还可能引入格式化注入)。要原样打印多个值就用 fmt.Println(vname1, vname2, vname3)。
顺带认一下 fmt 的错误标记体系,看到它们就知道是哪类问题:
| 标记 | 含义 |
|---|---|
%!(EXTRA type=value, ...) |
参数比动词多 |
%!v(MISSING) |
动词比参数多 |
%!d(string=abc) |
动词和参数类型不匹配(这里想打整数却给了字符串) |
%!(BADINDEX) |
显式参数索引 %[3]d 越界 |
%!(NOVERB) |
% 后面没跟动词 |
3. 规范
变量
声明,初始化,赋值
同一行声明多个变量并赋值: var a,b,c int =1,2,3 或者 d,e := 4,5
全局变量声明必须使用
var关键字,局部变量可以用短变量声明省掉它:a, b, c := 1, 2, 3。
注意省掉var之后就不能再写类型了——a, b, c int = 1, 2, 3在函数体里是语法错误,
要显式标类型就得把var写回来:var a, b, c int = 1, 2, 3变量
_表示丢弃
变量类型转换
- 类型转换必须是显示的,不存在隐式转换
- 只发生在兼容类型之间
- <变量名称> [:]= <目标类型>(<需要转换的变量>)
1 | package main |
变量可见性规则
- 大写字母开头的变量可导出,其他包可以读取,是共有变量
- 小写字母开头的变量不可导出,是私有变量
常量
常量定义形式
常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型
- 显示 const identifier [type] = value
- 隐式 const identifier = value (通常叫做 无类型常量)
- 常量可以使用 内置表达式定义 eg:
len(),unsafe.Sizeof()等
特殊常量 iota
- iota 在 const 关键字出现时被重置为 0
- const 中每新增一行常量声明 iota 就计数一次
- iota 只能在常量中使用
iota 常见使用法:
- 跳值使用法
- 插队使用法
- 表达式隐式使用法
- 单行使用法
4. package与import
package
package是最基本的分发单位 和 工程管理中依赖关系的体现- 每个
GO语言源代码文件开头 都拥有一个package声明,表示源代码所属的代码包 - 要生成
GO语言可执行程序,必须要有main的package包,且必须在该包下 有main()函数 - 同一个路径下(文件夹) 只能存在 一个
package, 一个package可以拆分为多个源文件组成
import 导入包的几种方式:点,别名与下划线
在写 Go 代码的时候经常用到 import 这个命令用来导入包文件,看到的方式参考如下:
1 | import( |
然后在代码里面可以通过如下的方式调用fmt.Println("hello world")
上面这个fmt是Go语言的标准库,他其实是去 GOROOT 下去加载该模块,当然 Go 的 import 还支持如下两种方式来加载自己写的模块:
相对路径 import "./model" // 当前文件同一目录的 model 目录,但是不建议这种方式 import
绝对路径 import "shorturl/model" // 加载GOPATH/src/shorturl/model模块
三种导入包的使用方法。
点操作
import( . "fmt" )
可以省略前缀的包名,fmt.Println("hello world")可以省略的写成Println("hello world")别名操作
import( f "fmt" )调用包函数时前缀变成了重命名的前缀,即f.Println("hello world")_ 操作
import ( "database/sql" _ "github.com/ziutek/mymysql/godrv" )
_ 操作其实只是引入该包。即使用 _ 操作引用包是无法通过包名来调用包中的导出函数,而是只是为了简单的调用其 init 函数()
5. 指针
变量是一种使用方便的占位符,用于引用计算机内存地址。
Go 语言的取地址符是 &,放到一个变量前使用就会返回相应变量的内存地址
1 | package main |
指针声明 : var var_name *var-type eg: var fp *float32
1 | package main |
空指针 :当一个指针被定义后没有分配到任何变量时,它的值为 nil
1 | package main |