Golang 学习笔记 1 代码风格
Table of Contents
1 项目文件夹结构
go 项目文件夹至少包括 3 个文件夹。针对个人开发:
- /bin 存放编译生成的二进制文件;
- /pkg 存放编译后生成的归档文件;
- /src 存放源代码:
- 网站域名 a:
- 作者 1:
- 项目 1:
- 模块 1
- 模块 2
- 模块 3
- ...
- 项目 2: ...
- 项目 3: ...
- ...
- 项目 1:
- 作者 2: ...
- 作者 3: ...
- ...
- 作者 1:
- 网站域名 b: ...
- 网站域名 c: ...
- ....
- 网站域名 a:
2 名称和声明
命名规则:
- 名字以大写字母开头,则在包外可访问;
- 名字以小写字母开头,不可在包外访问。
声明 给一个实体命名,然后设定其属性:
- 一个实体在函数内声明,那么它只在函数内部有效;
- 一个实体在函数外声明,那么它对包里的所有文件可见;
主要的声明关键字: 变量 var
,常量 const
,类型 type
和函数 func
。一个文件夹只能含有一个包,包名是每个 .go 文件的开头。
3 变量声明
变量声明以后会被初始化为各个类型的零值:
- 数字 -
0
- 字符串 -
""
- boolean -
false
- slice, map, 指针, 通道, 函数 -
nil
var name type = expression
中 type
和 expression
可以省略一个。主要用来给初始化表达式的类型与声明类型不同的变量赋值,或者用于初始值不重要以及后面才对变量赋值的情况。
name := expression
中 name
的类型由 expression
的类型决定。
// main 包和 func main() {...} 函数只有一个,是程序执行的入口。
// 一个文件夹下只能有一个包。
package main
import "fmt"
const boilingC = 100 // main 包中,包级别的声明
func main(){
s := "" // 常在包内部使用,变量重要
var s string // 常在包内部使用,变量不重要,以后赋值
var s = "" // 很少用,除非声明多个变量
var s string = "" // 类型一致下多余,类型不一致下必须
i, j := 0, 1 // 多变量声明和初始化
i, j = j, i // 交换两个变量的值
}
短变量声明不需要声明所有左边的变量。 对于第一个 err
变量,代码行为是 声明并赋值 ;对于第二个 err
变量,代码行为是 赋值 。
in, err := os.Open(infile)
//...
out, err := os.Create(outfile)
短变量最少需要声明一个变量,否则编译无法通过:
f, err := os.Open(infile)
//...
f, err := os.Create(outfile) // 编译错误,没有新的变量
f, err = os.Create(outfile) // 编译通过
3.1 指针声明
指针的值是一个变量的地址,指针指示值保存的位置。不是所有的值都有地址,但是所有的变量都有。
x := 3
p := &x // p 类型为 *int
*p = 2 // x = 2
指针是可以比较的,两个指针,当且仅当,指向同一个变量,或者两者都是 nil 的情况下相等。
fmt.Println(&x == &x, &x == &y, &x == nil) // `true false false`
3.2 new 函数
new
函数可以创建变量, new(T)
创建一个未命名的 T
类变量,初始化为其 空值 ,并且 返回其地址 。很少用。
3.3 赋值与多重赋值
最简单的形式是 =
:
x = 1 // 有名称的变量
*p = true // 间接变量
person.name = "name" // 结构体成员
count[x] = count[x] * scale // 数组或 slice 或 map 的元组
进阶形式:
count[x] *= scale
v := 1
v++
v--
多重赋值允许多个变量一次性被赋值:
x, y = y, x // 交换两个变量的值
j, k, l = 1, 2, 3 // 变量赋值更紧凑
f, err = os.Open("foo.text") // 调用返回两个值的函数
v, ok = m[key] // map 查询
v, ok = x.(T) // 类型断言
v, ok = <-ch // 通道接收
可赋值性: 指等号左右两边的变量为同一类型才可以进行赋值。类似的, ==
和 !=
必须要两边同一类型(可以双向赋值)才能进行比较。
4 声明新类型
type name underlying-type
定义一个新的类型:
type var1 float64
type var2 float64
const (
a var1 = 1.1
b var2 = 2.2
)
func fun(){
var c var1;
c = b // 报错,类型不一样
c = var1(b) // 必须要使用类型转换
}
尽管 var
和 var2
有相同的底层类型,但是他们依旧是两个不同的类型: 不能使用算数表达式进行合并。
对于每个类型 T
,都有一个对应的类型转换操作 T(x)
可以将 x
转换为 T
类型。如果两个类型具有相同的底层类型,或二者都是指向相同底层类型变量的未命名的指针类型,则可以相互转换。
5 运算符优先级
// 运算符优先级排序 * / % << >> & &^ + - | ^ == != < <= > >= && ||
其中:
^
作为二元运算符表示按位异或,作为一元运算符表示操作数逐位取反;&^
表示异或;x<<n
或x>>n
中,n
必须为无符号类型,运算结果向下取整:- 左移或右移用
0
补右边位, - 符号数右移用符号位补位。
- 左移或右移用