第二章 程序结构
2.1命名
划重点
- 函数名、变量名、常量名、类型名、语句标号和包名
- 以一个字母(Unicode字母)或下划线开头,跟任意数量的字母、数字或下划线,大写字母和小写字母是不同的。
- 关键字(25个)
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
- 预定义的名字 (30+个),这些不是关键字,可以再定义中重新使用它们
内建常量: true false iota nil 内建类型: int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 uintptr float32 float64 complex128 complex64 bool byte rune string error 内建函数: make len cap new append copy close delete complex real imag panic recover
- 作用域:内部定义内部有效;外部定义包内有效,名字开头大写可在包外部可见。比如:
fmt
的Print
可以在fmt
包外访问。 - 名字长度:原则上没有规定,但是内部尽可能短;如果作用域大,生命周期比较长,长名字会更有意义。
- 命名方式:使用 “驼峰式”命名。例如:标准库有
QuoteRuneToASCII
和parseRequestLine
这样的函数命名,但是一般不会用quote_rune_to_ASCII
和parse_request_line
这样的命名。而像ASCII
和HTML
这样的缩略词则避免使用大小写混合的写法,它们可能被称为htmlEscape
、HTMLEscape
或escapeHTML
,但不会是escapeHtml
。
2.2声明
划重点
- 类型声明:四种类型的声明语句:
var
、const
、type
和func
,分别对应变量、常量、类型和函数实体对象。 - 包一级类型、变量、常量、函数的各种类型的声明语句的顺序无关紧要,函数内部的名字则必须先声明之后才能使用。
- 函数声明:函数名字、参数列表、返回值列表、函数定义的函数体。
2.3变量
划重点
var 变量名字 类型 = 表达式
,“类型”或“= 表达式”可省其一,未初始化,则初始化为零值。- 零值:
数值-> 0
布尔->false
字符串->空字符串
接口或引用类型(包括slice、map、chan和函数)->nil
数组或结构体等聚合类型->各元素或字段对应的零值
- 声明一组变量,或者由函数返回的多个返回值初始化
var i, j, k int // int, int, int var b, f, s = true, 2.3, "four" // bool, float64, string var f, err = os.Open(name) // os.Open returns a file and an error
2.3.1 简短变量声明
划重点
- 示例,一个或一组变量:
i := 100 i, j := 0, 1
:=
是一个变量声明语句,而=
是一个变量赋值操作- 下面的代码先声明了in和err,则在第二次只声明out,已声明的err只是赋值; 简短变量声明中必须至少有一个新的变量;
编译通过 in, err := os.Open(infile) // ... out, err := os.Create(outfile)
编译失败 f, err := os.Open(infile) // ... f, err := os.Create(outfile) // compile error: no new variables
2.3.2指针
划重点
var x int
&x
表达式(取x
变量的内存地址),指针对应的数据类型是*int
,*p
表达式对应p指针指向的变量的值- Go语言中,返回函数中局部变量的地址也是安全的,下面的代码是有效的,但是每次返回的地址是变化的,值不变
var p = f() func f() *int { v := 1 return &v }
- Go语言中不能对指针
++
或--
,*p++
只是只是增加p指向的变量的值,并不改变p指针!!! - 指针是实现标准库中flag包的关键技术
常用库及方法
flag
flag.Bool()
flag.String()
flag.Parse()
flag.Args()
2.3.3new函数
划重点
new(T)
将创建一个T
类型的匿名变量,初始化为T
类型的零值,然后返回变量地址,返回的指针类型为*T
。- 用new创建变量和普通变量声明语句方式创建变量没有区别,下面是等价的:
func newInt() *int { return new(int) } func newInt() *int { var dummy int return &dummy }
- 如果两个类型都是空的,也就是说类型的大小是0,例如
struct{}
和[0]int
,有可能有相同的地址。(请谨慎使用大小为0的类型,因为如果类型的大小位0好话,可能导致Go语言的自动垃圾回收器有不同的行为,具体请查看 runtime.SetFinalizer 函数相关文档) new
为预定义的函数,不是关键字,将new
重新定义
func delta(old, new int) int { return new - old }
2.3.4变量的生命周期
划重点
- 包级变量:程序运行周期;局部变量:声明开始,不再引用为止,入参和返回均为局部变量;
- 函数右小括弧可以另起一行缩进,为了防止编译器在行尾自动插入分号而导致的编译错误,可以在末尾的参数变量后面显式插入逗号,如下:
img.SetColorIndex( size+int(x*size+0.5), size+int(y*size+0.5), blackIndex, // 最后插入的逗号不会导致编译错误,这是Go编译器的一个>特性 ) // 小括弧另起一行缩进,和大括弧的风格保存一致
- Go编译器会自动选择在栈上还是在堆上分配局部变量的存储空间,但可能令人惊讶的是,这个选择并不是由用
var
还是new
声明变量的方式决定 - 变量的逃逸行为
2.4赋值
划重点
- 自增和自减是语句,而不是表达式,因此
x = i++
之类的表达式是错误的
2.4.1元组赋值
划重点
- 同时更新变量左边的值
x, y = y, x a[i], a[j] = a[j], a[i]
- 表达式会产生多个值,或者返回布尔值,通常被称为ok,map查找(§4.3)、类型断言(§7.10)、或通道接收(§8.4.2) 出现在赋值语句的右边,它们都可能会产生两个结果,有一个额外的布尔结果表示操作是否成功:
f, err = os.Open("foo.txt") // function call returns two values v, ok = m[key] // map lookup v, ok = x.(T) // type assertion v, ok = <-ch // channel receive
- :map查找(§4.3)、类型断言(§7.10)或通道接收(§8.4.2)出现在赋值语句的右边时,并不一定是产生两个结果,也可能只产生一个结果。对于值产生一个结果的情形,map查找失败时会返回零值,类型断言失败时会发送运行时panic异常,通道接收失败时会返回零值(阻塞不算是失败),例如:
v = m[key] // map查找,失败时返回零值 v = x.(T) // type断言,失败时panic异常 v = <-ch // 管道接收,失败时返回零值(阻塞不算是失败) _, ok = m[key] // map返回2个值 _, ok = mm[""], false // map返回1个值 _ = mm[""] // map返回1个值
2.4.2可赋值性
划重点
- 类型必须完全匹配,nil可以赋值给任何指针或引用类型的变量
- 两个值是否可以用
==
或!=
进行相等比较的能力也和可赋值能力有关系:对于任何类型的值的相等比较,第二个值必须是对第一个值类型对应的变量是可赋值的
2.5类型
划重点
type 类型名字 底层类型
类型声明语句一般出现在包一级,因此如果新创建的类型名字的首字符大写,则在外部包也可以使用- 每一个类型
T
,都有转换操作T(x)
,用于将x
转为T
类,如果T
是
指针类型,可能会需要用小括弧包装T
,比如(*int)(0)
,当两个类型的底层基础类型相同时,才允许这种转型操作
2.6包和文件
划重点
- 包所在目录路径的后缀是包的导入路径;例如包
gopl.io/ch1/helloworld
对应的目录路径是$GOPATH/src/gopl.io/ch1/helloworld
- 每个包都对应一个独立的名字空间;
image.Decode
或utf16.Decode
不同的形式 - 如果一个名字是大写字母开头的,那么该名字是导出的
- 包级别的名字,在一个文件声明的类型和常量,在同一个包的其他源文件也是可以直接访问的
- 包声明前的注释是包注释(§10.7.4)。一个包通常只有一个源文件有包注释,如果有多个包注释,会将它们链接为一个包注释,如果很大,通常会放到独立的doc.go文件中
2.6.1导入包
划重点
- 每个包还有一个包名,包名一般是短小的名字(并不要求包名是唯一的)
golang.org/x/tools/cmd/goimports
导入工具,可以根据需要自动添加或删除导入的包
2.6.2包的初始化
划重点
- 包的初始化解决包级变量的依赖顺序,并根据出现的顺序依次初始化
var a = b + c // a 第三个初始化, 为 3 var b = f() // b 第二个初始化, 为 2, 通过调用 f (依赖c) var c = 1 // c 第一个初始化, 为 1 func f() int { return c + 1 }
- 某些表格数据初始化不是简单的赋值过程,可以用一个特殊的init初始化函数来简化初始化工作,每个文件都可以包含多个init初始化函数,在程序开始执行时按照它们声明的顺序被自动调用,每个包只会被初始化一次
func init() { /* ... */ }
- range循环只使用了索引,则可以省略没有用到的值部分,下面等价:
for i := range pc {}
for i, _ := range pc { }
2.7作用域
划重点
- 一个程序可能包含多个同名的声明,只要作用域不一样即可。例:你可以声明一个局部变量,和包级的变量同名;可以将一个函数参数的名字声明为
new
,虽然内置的new
是全局作用域的。局部声明覆盖全局声明; if
,switch
条件部分为一个隐式词法域,然后每个是每个分支的词法域- 在包级别,声明的顺序并不会影响作用域范围,因此一个先声明的可以引用它自身或者是引用后面的一个声明,这可以让我们定义一些相互嵌套或递归的类型或函数。
常用库及方法
os
os.Getwd
log
log.Fatalf
unicode
unicode.ToUpper
来源:CSDN
作者:rabbit0206
链接:https://blog.csdn.net/rabbit0206/article/details/103758291