Go 程序结构

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. 作用域

  • 14
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值