diff --git a/eBook/04.2.md b/eBook/04.2.md index b11689b..3516d86 100644 --- a/eBook/04.2.md +++ b/eBook/04.2.md @@ -1,7 +1,7 @@ # 4.2 Go 程序的基本结构和要素 示例 4.1 [hello_world.go](examples/chapter_4/hello_world.go) - + ```go package main @@ -9,7 +9,7 @@ import "fmt" func main() { fmt.Println("hello, world") -} +} ``` ## 4.2.1 包的概念、导入与可见性 @@ -50,31 +50,31 @@ Go 中的包模型采用了显式依赖关系的机制来达到快速编译的 `import "fmt"` 告诉 Go 编译器这个程序需要使用 `fmt` 包(的函数,或其他元素),`fmt` 包实现了格式化 IO(输入/输出)的函数。包名被封闭在半角双引号 `""` 中。如果你打算从已编译的包中导入并加载公开声明的方法,不需要插入已编译包的源代码。 如果需要多个包,它们可以被分别导入: - + ```go import "fmt" import "os" -``` +``` 或: - + ```go import "fmt"; import "os" -``` +``` 但是还有更短且更优雅的方法(被称为因式分解关键字,该方法同样适用于 const、var 和 type 的声明或定义): - + ```go import ( "fmt" "os" -) +) ``` -它甚至还可以更短的形式,但使用 gofmt 后将会被强制换行: - -```go -import ("fmt"; "os") +它甚至还可以更短的形式,但使用 gofmt 后将会被强制换行: + +```go +import ("fmt"; "os") ``` 当你导入多个包时,导入的顺序会按照字母排序。 @@ -102,19 +102,19 @@ import ("fmt"; "os") 你可以通过使用包的别名来解决包名之间的名称冲突,或者说根据你的个人喜好对包名进行重新设置,如:`import fm "fmt"`。下面的代码展示了如何使用包的别名: 示例 4.2 [alias.go](examples/chapter_4/alias.go) - + ```go package main - + import fm "fmt" // alias3 func main() { fm.Println("hello, world") -} +} ``` -**注意事项** - +**注意事项** + 如果你导入了一个包却没有使用它,则会在构建程序时引发错误,如 `imported and not used: os`,这正是遵循了 Go 的格言:“没有不必要的代码!“。 **包的分级声明和初始化** @@ -123,10 +123,10 @@ func main() { ## 4.2.2 函数 -这是定义一个函数最简单的格式: - -```go -func functionName() +这是定义一个函数最简单的格式: + +```go +func functionName() ``` 你可以在括号 `()` 中写入 0 个或多个函数的参数(使用逗号 `,` 分隔),每个参数的名称后面必须紧跟着该参数的类型。 @@ -139,28 +139,28 @@ main 函数是每一个可执行程序所必须包含的,一般来说都是在 函数里的代码(函数体)使用大括号 `{}` 括起来。 -左大括号 `{` 必须与方法的声明放在同一行,这是编译器的强制规定,否则你在使用 gofmt 时就会出现错误提示: - +左大括号 `{` 必须与方法的声明放在同一行,这是编译器的强制规定,否则你在使用 gofmt 时就会出现错误提示: + `build-error: syntax error: unexpected semicolon or newline before {` (这是因为编译器会产生 `func main() ;` 这样的结果,很明显这错误的) - + **Go 语言虽然看起来不使用分号作为语句的结束,但实际上这一过程是由编译器自动完成,因此才会引发像上面这样的错误** -右大括号 `}` 需要被放在紧接着函数体的下一行。如果你的函数非常简短,你也可以将它们放在同一行: - -```go -func Sum(a, b int) int { return a + b } +右大括号 `}` 需要被放在紧接着函数体的下一行。如果你的函数非常简短,你也可以将它们放在同一行: + +```go +func Sum(a, b int) int { return a + b } ``` 对于大括号 `{}` 的使用规则在任何时候都是相同的(如:if 语句等)。 因此符合规范的函数一般写成如下的形式: - + ```go func functionName(parameter_list) (return_value_list) { … -} +} ``` 其中: @@ -170,10 +170,10 @@ func functionName(parameter_list) (return_value_list) { 只有当某个函数需要被外部包调用的时候才使用大写字母开头,并遵循 Pascal 命名法;否则就遵循骆驼命名法,即第一个单词的首字母小写,其余单词的首字母大写。 -下面这一行调用了 `fmt` 包中的 `Println` 函数,可以将字符串输出到控制台,并在最后自动增加换行字符 `\n`: - -```go -fmt.Println("hello, workd") +下面这一行调用了 `fmt` 包中的 `Println` 函数,可以将字符串输出到控制台,并在最后自动增加换行字符 `\n`: + +```go +fmt.Println("hello, world") ``` 使用 `fmt.Print("hello, world\n")` 可以得到相同的结果。 @@ -191,15 +191,15 @@ fmt.Println("hello, workd") ## 4.2.3 注释 示例 4.2 [hello_world2.go](examples/chapter_4/hello_world2.go) - -```go -package main -import "fmt" // Package implementing formatted I/O. +```go +package main + +import "fmt" // Package implementing formatted I/O. func main() { fmt.Printf("Καλημέρα κόσμε; or こんにちは 世界\n") -} +} ``` 上面这个例子通过打印 `Καλημέρα κόσμε; or こんにちは 世界` 展示了如何在 Go 中使用国际化字符,以及如何使用注释。 @@ -211,25 +211,25 @@ func main() { 每一个包应该有相关注释,在 package 语句之前的块注释将被默认认为是这个包的文档说明,其中应该提供一些相关信息并对整体功能做简要的介绍。一个包可以分散在多个文件中,但是只需要在其中一个进行注释说明即可。当开发人员需要了解包的一些情况时,自然会用 godoc 来显示包的文档说明,在首行的简要注释之后可以用成段的注释来进行更详细的说明,而不必拥挤在一起。另外,在多段注释之间应以空行分隔加以区分。 示例: - + ```go // Package superman implements methods for saving the world. // // Experience has shown that a small number of procedures can prove // helpful when attempting to save the world. package superman -``` +``` 几乎所有全局作用域的类型、常量、变量、函数和被导出的对象都应该有一个合理的注释。如果这种注释(称为文档注释)出现在函数前面,例如函数 Abcd,则要以 `"Abcd..."` 作为开头。 示例: - + ```go // enterOrbit causes Superman to fly into low Earth orbit, a position // that presents several possibilities for planet salvation. func enterOrbit() error { ... -} +} ``` godoc 工具(第 3.6 节)会收集这些注释并产生一个技术文档。 @@ -243,57 +243,57 @@ godoc 工具(第 3.6 节)会收集这些注释并产生一个技术文档。 结构化的类型没有真正的值,它使用 nil 作为默认值(在 Objective-C 中是 nil,在 Java 中是 null,在 C 和 C++ 中是NULL或 0)。值得注意的是,Go 语言中不存在类型继承。 函数也可以是一个确定的类型,就是以函数作为返回类型。这种类型的声明要写在函数名和可选的参数列表之后,例如: - + ```go func FunctionName (a typea, b typeb) typeFunc -``` +``` 你可以在函数体中的某处返回使用类型为 typeFunc 的变量 var: - + ```go return var -``` +``` -一个函数可以拥有多返回值,返回类型之间需要使用逗号分割,并使用小括号 `()` 将它们括起来,如: - -```go -func FunctionName (a typea, b typeb) (t1 type1, t2 type2) +一个函数可以拥有多返回值,返回类型之间需要使用逗号分割,并使用小括号 `()` 将它们括起来,如: + +```go +func FunctionName (a typea, b typeb) (t1 type1, t2 type2) ``` 示例: 函数 Atoi (第 4.7 节):`func Atoi(s string) (i int, err error)` -返回的形式: - -```go -return var1, var2 +返回的形式: + +```go +return var1, var2 ``` 这种多返回值一般用于判断某个函数是否执行成功(true/false)或与其它返回值一同返回错误消息(详见之后的并行赋值)。 使用 type 关键字可以定义你自己的类型,你可能想要定义一个结构体(第 10 章),但是也可以定义一个已经存在的类型的别名,如: - + ```go -type IZ int +type IZ int ``` **这里并不是真正意义上的别名,因为使用这种方法定义之后的类型可以拥有更多的特性,且在类型转换时必须显式转换。** -然后我们可以使用下面的方式声明变量: - -```go -var a IZ = 5 +然后我们可以使用下面的方式声明变量: + +```go +var a IZ = 5 ``` 这里我们可以看到 int 是变量 a 的底层类型,这也使得它们之间存在相互转换的可能(第 4.2.6 节)。 如果你有多个类型需要定义,可以使用因式分解关键字的方式,例如: - + ```go type ( IZ int FZ float64 STR string -) +) ``` 每个值都必须在经过编译后属于某个类型(编译器必须能够推断出所有值的类型),因为 Go 语言是一种静态类型语言。 @@ -310,37 +310,37 @@ type ( - 然后定义其余的函数,首先是类型的方法,接着是按照 main 函数中先后调用的顺序来定义相关函数,如果有很多函数,则可以按照字母顺序来进行排序。 示例 4.4 [gotemplate.go](examples/chapter_4/gotemplate.go) - + ```go -package main +package main import ( "fmt" -) +) -const c = "C" +const c = "C" -var v int = 5 +var v int = 5 -type T struct{} +type T struct{} func init() { // initialization of package -} +} func main() { var a int Func1() // ... fmt.Println(a) -} +} func (t T) Method1() { //... -} +} func Func1() { // exported function Func1 //... -} +} ``` Go 程序的执行(程序启动)顺序如下: @@ -353,29 +353,29 @@ Go 程序的执行(程序启动)顺序如下: ## 4.2.6 类型转换 在必要以及可行的情况下,一个类型的值可以被转换成另一种类型的值。由于 Go 语言不存在隐式类型转换,因此所有的转换都必须显式说明,就像调用一个函数一样(类型在这里的作用可以看作是一种函数): - + ```go valueOfTypeB = typeB(valueOfTypeA) -``` +``` **类型 B 的值 = 类型 B(类型 A 的值)** 示例: - + ```go a := 5.0 b := int(a) -``` +``` 但这只能在定义正确的情况下转换成功,例如从一个取值范围较小的类型转换到一个取值范围较大的类型(例如将 int16 转换为 int32)。当从一个取值范围较大的转换到取值范围较小的类型时(例如将 int32 转换为 int16 或将 float32 转换为 int),会发生精度丢失(截断)的情况。当编译器捕捉到非法的类型转换时会引发编译时错误,否则将引发运行时错误。 具有相同底层类型的变量之间可以相互转换: - + ```go var a IZ = 5 c := int(a) d := IZ(c) -``` +``` ## 4.2.7 Go 命名规范