Go 程序结构

发布时间:2024年01月23日

1. 名称

名称的命名规则

在 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 函数??

2. 声明?

声明的含义声明给一个程序实体命名,并设定该实体的部分或全部属性
声明的分类

在 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
}

3. 变量

3.1 基本语法
变量的基本语法
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 返回一个文件句柄和一个错误

3.2 短变量声明
短变量声明(局部变量)
概念在函数中,一种称作 "短变量声明" 的可选形式,可以用来声明和初始化局部变量
语法

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)?

3.3 指针

3.4 new 函数
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 函数是不可用的

3.5 变量的生命周期

4. 赋值

5. 类型声明

6. 包和文件

在 Go 语言中,包的作用和其他语言中的库(C/C++)或模块(Java/Python)的作用类似,用于支持模块化 、封装 、编译隔离和重用
一个包的源代码,保存在一个或多个以 .go 结尾的文件中,源文件所在目录名的尾部就是包的导入路径
每一个包,给其所含有的声明,提供了独立的命名空间

7. 作用域

文章来源:https://blog.csdn.net/yzr213/article/details/135740596
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。