Go语言圣经 .1.程序结构
程序结构
命名
关键字
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 |
常量、类型和函数
- 常量: true, false, iota, nil
- 类型: int, int8, int16, int32, int64, unit, unit8, unit16, unit32, unit64, unitptr, float32, float64, complex128, complex64, bool, byte, rune, string, error
- 函数: make, len, cap, new, append, copy, close, delete, complex, real, imag, panic, recover
注意事项:
包名本身总是由小写组成的
实体第一个字母的大小写决定其可见性是否跨包。如果大写开头,它是导出的, 对外是可见和可访问的
例如:
fmt
包中的Printf
声明
声明给一个程序实体命名,并且设定其部分或者全部属性。
4个主要的声明:
- 变量(
var
) - 常量(
const
) - 类型(
type
) - 函数(
func
)
go程序存放在一个或者多个以.go
结尾的文件里,每个文件以package
声明开头,表明文件属于哪个包,package
声明之后是import
声明,然后是包级别的类型、变量、常量、函数的声明、不区分顺序。
package main |
变量
var
声明创建一个具体类型的变量,通用形式:
var name type = expression
其中类型(type)和表达式(express)只能省略一个
如果省略
type
,则变量的类型将由初始表达式决定如果省略
expression
, 则变量的初始值对应类型的零值
,各个类型的零值:
- 布尔 -
false
- 字符串 -
""
- 接口和引用类型(slice、指针、map、通道、函数) -
nil
- 数组或结构体一样的符合类型 - 其所有元素或成员的零值
例如:
var s string
fmt.Println(s) // ""- 布尔 -
短变量声明
短变量声明的形式:
name := expression
指针
变量是存储值的地方。
指针的值是一个变量的地址。
如果一个变量的声明为var x int
,
表达式 &x
(x的地址) 获取一个指向整型变量的指针,
它的类型是整型指针*int
指针p指向的变量写作 *p
x := 1 // |
指针类型的零值是nil
,
若p != nil
,则说明p指向一个变量。
指针是可比较的。两个指针当且仅当指向同一个变量或者两者都是nil的情况下才相等。
var x y int |
指针与变量的关系
- 声明变量:,
var x int
= 100, 编译器会在内存中留出一个唯一的地址单元来存储变量,如下,编译器地址为1004的内存单元留给变量,数值为100,将地址1004与该变量的值100关联起来
- 创建指针:变量x的地址是1004,是一个数字,地址的这个数字可以用另一个变量来保存它,假设这个变量为p,此时p未初始化,系统为它分配了空间,但值还不确定
- 初始化指针:将变量x的地址存储到变量p中,初始化后(p=&x),p指向x,称为一个指向x的指针。指针是一个变量,存储了另一个变量的地址。
- 声明指针:*p和x指向的是x的内容,p和&x指向的是x的地址
new 函数
另一种创建变量的方式是使用内置的new函数
表达式new(T)
创建一个未命名的T类型变量,初始化为T的零值,并返回其地址(地址类型为*T)
p := new(int) |
变量的生命周期
变量的声明周期是根据其是否可达来确定的。
赋值
x = 1 |
可赋值性
隐式赋值
medals := []string{"gold", "silver", "bronze"}
显式赋值
var a = 1
可赋值性的规则对于不同类型有着不同的要求:
类型必须完全匹配
nil可以赋值给任务指针或者引用类型的变量
常量有更加灵活的赋值规则
类型
类型声明语句:
type 类型名字 底层类型 |
对于每一个类型T,都有一个对应的类型转换操作T(x)
,
用于将x转换为T类型(如果T是指针类型,则可能需要用小括弧包装T,比如(*int)(0)
)
package tempconv |
下面的声明语句,Celsius类型的参数出现在了函数名(String)的前面,表示声明的是Celsius类型的一个叫做String的方法:
func (c Celsius) String() string { return fmt.Sprintf("%g ℃", c) } |
许多类型都会定义一个String方法,当使用fmt包打印的时候,会优先使用该类型的String方法
c := FToC(212.0) |
包和文件
- 通常一个包所在目录路径的后缀是包的导入路径;例如: 包 gopl.io/ch1/hellowworld对应的目录路径是 $GOPATH/src/gopl.io/ch1/helloworld
- 每个包对应一个独立的名字空间,例如,image包中的Decode函数和在unicode/utf16包中的Decode函数是不同的。要在外部引用该函数,必须显示的使用image.Decode或utf16.Decode
- 包可以用过控制名字首字母来控制外部可见的信息。大写开头,名字是导出的,小写则是隐藏
在每个源文件的包声明前紧跟着的注释是包注释
如果包注释很大, 通常会放到一个独立的doc.go
文件中
导入包
每个包都是有一个全局唯一的导入路径。导入语句中类似gopl.io/ch2/tempconv
的字符串对应包的导入路径,
每个包还有一个包名,包名一般是短小的名字,一般包名跟导入路径最后一个字段相同,例如`gopl.io/ch2/tempconv
的包名是tempconv
要使用gopl.io/ch2/tempconv包,需要先导入:
package main |
如果导入了一个包,但是又没有使用该包,将被当作编译错误来处理。
包的初始化
包的初始化首先是解决包级变量的依赖顺序,然后按照包级变量声明出现的顺序依次初始化:
var a = b + c // a 第三个初始化, 为 3 |
作用域
var cwd string |
这里的全局变量cwd没有被正确初始化,
可以调整为
var cwd string |
单独声明err变量,避免使用:=
短声明
练习
练习 2.1:
向tempconv包添加类型、常量和函数用来处理Kelvin绝对温度的转换,Kelvin 绝对零度是−273.15°C,Kelvin绝对温度1K和摄氏度1°C的单位间隔是一样的。
练习 2.2:
写一个通用的单位转换程序,用类似cf程序的方式从命令行读取参数,如果缺省的 话则是从标准输入读取参数,然后做类似Celsius和Fahrenheit的单位转换,长度单位可以对应英尺和米,重量单位可以对应磅和公斤等
练习 2.3:
重写PopCount函数,用一个循环代替单一的表达式。比较两个版本的性能。(11.4节将展示如何系统地比较两个不同实现的性能。)
练习 2.4:
用移位算法重写PopCount函数,每次测试最右边的1bit,然后统计总数。比较和查表算法的性能差异。
练习 2.5:
表达式 x&(x-1) 用于将x的最低的一个非零的bit位清零。使用这个算法重写PopCount函数,然后比较性能。