名称的命名规则 | 在 Go 语言中,名称包括: 函数 、变量 、常量 、类型 、语句标签 、包等 |
(i). 以字母或下划线开头,后面是任意数量的字符 、数字 、下划线(字符、数字、下划线,但不能以数字开头) (ii). 区分大小写(heapSort 与 Heapsort 不同) (iii). 不能使用关键字作为名称 (iv). 不建议使用预声明作为名称 (v).??包名称总是由小写字母组成 (vi). 名称本身没有长度限制,但倾向于使用短名称,尤其是作用域较小的局部变量 ? ? ? ?名称的作用范围越大,就是用越长且更有意义的名称 (vii). 当遇到由单词组合的名称时,使用 "驼峰式" 的风格,不要用下划线分隔单词 ? ? ? ? 而是名称的第一个单词的首字母小写,剩余单词的首字母大写 | |
关键字 | 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 ? ? ? ? ? ? ?uint? ? uint8? ? uint16? ? uint32? ? uint64? ? uintptr ? ? ? ? ? ? ?float32? ? float64? ? complex64? ? complex128 ? ? ? ? ? ? ?bool? ? byte? ? rune? ? string? ? error 函数 :make? ? len? ? cap? ? new? ? append? ? copy? ? close? ? delet ? ? ? ? ? ? complex? ? real? ? imag ? ? ? ? ? ? panic? ? recover |
这些名称不是预留的,可以在声明中使用它们; 很多地方对其中的名称进行了重声明,但发生名称冲突的风险很大 | |
名称的作用范围 | 函数内声明 :如果一个实体(变量)在函数内声明,该变量只在函数局部有效 |
函数外声明 :如果一个实体(变量)在函数外声明,该变量对当前包中的所有 源文件可见(全局变量) | |
注意 :标识符的第一个字母的大小决定了该实体可见性是否跨包 ? ? ? ? ? ?所谓跨包,就是其他包中的代码是否能访问当前包中的这个对象 (i).??如果实体的名称是以小写字母开头,该实体只能被当前包中的代码访问(私有的) (ii). 如果实体的名称是以大写字母开头,该实体是导出的(公有的),当前包内的代码以及其他包中的代码都可以访问该实体(首先要导入含有该实体的包) ? ? ? ? ? ? 示例 :fmt 包中的 Printf 函数,就是导出的,任何文件只要导入 fmt 包,该文件中的代码就可以使用 Printf 函数?? |
声明的含义 | 声明给一个程序实体命名,并设定该实体的部分或全部属性 |
声明的分类 | 在 Go 语言中,有 4 个主要的声明: 变量( var )、常量( const )、类型( type?)、函数( func ) |
Go 语言程序 的组织形式 | (i).??Go 程序存储在一个或多个以 " .go? " 为后缀的文件里 (ii).? Go 代码是用包来组织的,包类似于其他语言中的库和模块? (iii).? 一个包由一个或多个 " .go?" 源文件组成,放在一个文件夹中, ? ? ? ? 该文件夹的名字描述了包的作用 ? ? ?(所谓包,就是一个包含一个或多个 .go 源文件的文件夹) |
(iv). 每一个 .go 源文件,除注释外的第一条语句,都是 package 声明, ? ? ? ?指明了当前 .go 源文件属于哪个包 (v). 名为 main 的包比较特殊,main 包用来定义一个独立的可执行程序,而不是库 (vi). 在 main 包中,main 函数也是特殊的,因为 main 函数总是程序开始执行的入口, ? ? ? ?main 函数可以调用其他包中的函数来做更多事情,比如 fmt.Println? | |
(vii). package 声明后面是 import 声明,用来告诉编译器当前源文件需要导入哪些包 (viii). import 声明必须跟在 package 声明之后 (ix).? 必须精确地导入所需要的包,在缺失导入或存在不需要的包的情况下, ? ? ? ?编译都会失败(这样可以防止程序演化中引用不需要的包) | |
(iv). 在 import 导入声明之后,是 "包级别" 的类型 、变量 、常量 、函数的声明, ? ? ? ?编译器不关心这些包级别声明的顺序 | |
函数声明 | 函数的声明由 func 关键字 、函数名 、参数列表(main 函数为空,函数调用者提供实参)、可选的返回值列表(可以为空)、放在花括号内的函数体组成,函数体定义函数的功能; 如果函数不返回任何内容,返回值列表可以省略; 函数的执行从第一个语句开始,直到遇到一个 return 语句,或者执行到无 return 语句的函数的结尾,然后程序控制权和返回值(如果存在)都返回给调用者 |
Go 语言特性 | Go 语言中,不需要在语句或声明后面使用分号结尾 当多个语句或声明出现在同一行时,用分号隔开(类似于 Python) 事实上,跟在特定符号后面的换行符被转换为分号,在什么地方进行换行会影响对 Go 代码的解析 |
左花括号 ' { ' 必须和关键字 func 在同一行,不能独立成行; 在表达式 x + y 中,换行符可以在 + 操作符的后面,但不能在 + 操作符的前面 |
示例:
// ftoc 输出两个华氏温度 - 摄氏温度的转换
package main
import "fmt"
func main() {
const freezingF, boilingF = 32.0, 212.0
fmt.Printf("%gF = %gC\n", freezingF, fToC(freezingF)) // "32F = 0C"
fmt.Printf("%gF = %gC\n", boilingF, fToC(boilingF)) // "212F = 100C"
}
func fToC(f float64) float64 {
return (f - 32) * 5 / 9
}
变量的基本语法 | |
var 声明 | 关键字 var 声明一个具体类型的变量,然后给变量附加一个名字,并设置其初始值? |
每一个变量声明都有一个通用的形式: var? name? type? =? expression | |
初始化 | 类型 type 和表达式 expression 只能省略其中一个,不能两个同时省略 |
如果类型 type 省略,则变量 name 的类型由表达式 expression 推导出 var? name? =? expression | |
如果表达式 expression 省略,则变量 name 的初始值为类型 type 的 " 零值 " var name type 数字的零值为 0 ,布尔类型的零值为 false ,字符串的零值为 "" , 接口和引用类型(slice 、指针 、map 、通道 、函数)的零值为 nil | |
数组或结构体这样的复合类型的零值,是其所有元素或成员的零值 | |
零值机制 | 零值机制保证所有的变量都是良好定义的,Go 中不存在未初始化的变量; 这种机制简化了代码,且不需要额外工作就能感知边界条件的行为 |
零值示例 | var? s? string fmt.Println(s)? ? // " " 输出空字符串,而不是一些错误或不可预料的行为 |
零值说明 | Go 程序员经常花费精力来使复杂类型的零值有意义, 为了使变量一开始就处于可用状态 |
变量列表 多元声明 | 可以声明一个 "?变量列表 ",并使用对应的 "?表达式列表 " 来初始化该变量列表 |
忽略类型,允许声明多个不同类型的变量 | |
示例 | var? i,j,k? int? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //? int? int? int var? b,f,s? =? true,2.3,"four"? ? ?// bool? float64? string |
初始值范围 | 初始值设定可以是字面量值,也可以是任意的表达式 |
变量初始化 | 包级别的初始化在 main 函数开始之前进行 局部变量的初始化和声明一样,在函数执行期间进行 |
变量可以通过调用 " 返回多个值的函数 " 进行初始化 使用函数返回值来初始化变量: var? f,err? =? os.Open(filename)? // os.Open 返回一个文件句柄和一个错误 |
短变量声明(局部变量) | |
概念 | 在函数中,一种称作 "短变量声明" 的可选形式,可以用来声明和初始化局部变量 |
语法 | name? :=? expression 变量 name 的类型由初始化表达式 expression 推导得出 |
示例 | 下面是 lissajous 函数中的三个短变量声明: anim? :=? gif.GIF{LoopCount : nframes} freq? :=? rand.Float64()? *? 3.0 t? :=? 0.0 |
短变量声明 使用场景 | 因短小 、灵活,所以在局部变量的声明和初始化中,主要使用短变量声明 |
var 声明 使用场景 | (i).?为那些跟初始化表达式类型不一致的局部变量所保留的 (ii). 之后才对变量赋值(只声明变量,未给出特定初始值) (iii). 变量初始值不重要 |
示例 | i? :=? 100 var? boiling? float64? =? 100 var? name? []string var? err? error var? p? Point |
短变量 声明列表 | 与 var 声明一样,多个变量可以用短变量声明的方式,声明和初始化 区分多重赋值 i,j? :=? 0,1? ?// 短声明列表 x,y = 0,1? ?// 多重赋值 |
Tips | 当可以提高可读性时,才使用多个初始化表达式来进行变量声明; 例如,短小且天然一组的 for 循环的初始化 |
声明与赋值 | " := " 表示声明 ," = " 表示赋值 一个变量列表的短声明,不能和 " 多重赋值 " 搞混; 多重赋值将运算符右边的值,赋给运算符左边的对应变量 |
多元赋值 示例 | i,j = j,i? ? // 交换 i 和 j 的值 |
函数返回值 初始化变量 | 与普通的 var 声明类似,短变量声明也可以调用像 os.Open 那样返回多个值的函数, 进行初始化 |
示例 | f,err? :=? os.Open(filename) if? err? !=? nil? { ? ? return? err } //? ... 使用 f ... f.Close() |
短变量声明 特别注意点 | 短变量声明,不需要声明所有在左边的变量 |
再次声明 同名变量 等于赋值 | 如果一些变量在同一个词法块中(多次)声明,除第一次声明,其后的声明等于赋值 |
示例: in,err? :=? os.Open(infile) // ... out,err? :=? os.Create(outfile)? ? //? 这里的 err 声明等于赋值 | |
只有在同一个词法块中已经存在变量的情况下,短变量声明的行为才和赋值操作一样; (同一语句块中,再次声明同名变量,等同于赋值) 若内外层有同名的变量,则外层的声明将被忽略 | |
至少声明 一个新变量 | 短变量声明,至少声明一个新变量,否则代码编译会失败 |
示例: f,err? :=? os.Open(infile) // ... f,err? :=? os.Create(outfile)? // 编译错误:没有新的变量 // 使用赋值语句修复 f,err? =? os.Create(outfile)? |
new 函数 | ||
另外一种创建变量(申请内存)的方式,是使用内置的 neqw 函数 | ||
表达式 new(T) 创建了一个未命名的 T 类型变量(申请了一块大小为 T 的内存空间),初始化为 T 类型的零值,并返回该内存的地址(地址类型为 *T) | ||
p := new(int)? ? // *int 类型的指针 p ,指向未命名的 int 变量 fmt.Println(*p)? // 输出 "0" *p = 2? ? ? ? ? ? ? ?// 把为名,名的 int 变量设置为 2 fmt.Println(*p)? ?// 输出 "2" | ||
使用 new 创建的变量 、可以取地址的普通变量,这两者没什么不一样 | ||
普通变量有名字,可以用取地址符获取变量(内存)的地址; 可以用名字操作,也可以用地址操作 | ||
用 new() 函数申请的变量,只能用地址操作; 通过 new(T) 就可以直接在表达式中使用 | ||
下面两个 newInt 函数有同样的行为: | ||
func? newInt() *int { ? ? ? ? return? new(int) } | func? newInt() *int { ? ? ? ? var? dummy? int ? ? ? ? return? &dummy } | |
每一次调用 new 返回一个具有唯一地址的不同变量 | ||
p? :=? new(int) q? :=? new(int) fmt.Println(p?== q)? // "false"? p 和 q 是两块不同内存的地址 | ||
例外 | 当两个变量的类型不携带任何信息且是零值,例如 struct{} 或 [0]int ,当前的视线里面,它们有相同的地址 | |
因为最常见的未命名变量都是结构体类型,它的语法比较复杂,所以 new 函数使用得相对较少 | ||
new 是一个预声明的函数,不是一个关键字; 所以,new 函数可以重定义为另外的其他类型(成为一个标志符) | ||
func? delta(old ,new? int)? int? {? return? new - old? } 自然,在 delta 函数内,内置的 new 函数是不可用的 |
在 Go 语言中,包的作用和其他语言中的库(C/C++)或模块(Java/Python)的作用类似,用于支持模块化 、封装 、编译隔离和重用 | |
一个包的源代码,保存在一个或多个以 .go 结尾的文件中,源文件所在目录名的尾部就是包的导入路径 | |
每一个包,给其所含有的声明,提供了独立的命名空间 | |