Golang 25个保留关键字 - 云+社区 - 腾讯云

1.概览

Go共有25个保留关键字,各有其作用,不能用作标识符。Go的25个关键字按照作用可以分为3类,分别为包管理、程序实体声明与定义与程序流程控制。

包管理(2个):

import    package

程序实体声明与定义(8个):

chan    const    func    interface    map    struct    type    var

程序流程控制(15个):

break    case    continue    default    defer    else    fallthrough    
for        go        goto        if        range    return    select        switch

2.包管理

2.1 import

import 用于导入包,这样就可以使用包中被导出的标识符。导入格式如下:

import _ "package path"
import . "package path"
import alias "package path"

import (

_ "package path"
. "package path"
alias "package path"

)

其中包路径前面可以有三中修饰符中的某一个。下划线即空白标识符,表示不使用包中的标识符,只需要包的副作用,即计算包级变量的初始化表达式和执行导入包的init初始化函数。点号代替包的别名, 表示访问包中的导出标识符无需使用包名。alias则表示包的别名。

导入示例如下:

导入声明 Sin的本地名
import "lib/math" math.Sin
import m "lib/math" m.Sin
import . "lib/math" Sin

2.2 package

package用于声明包的名称,需放在go文件所有代码的最前面。一个包由一个或多个go源文件组成,需放在同一个目录下,且同一个目录下的这些go文件的package的名字只能有一个。申明格式如下:

package <packagename>

packagename不能为空白标识符_。

3.程序实体声明与定义

3.1 chan

chan用于声明channel(信道)。信道提供一种机制使两个并发执行的函数实现同步,并通过传递具体元素类型的值来通信。未初始化的信道值为 nil。声明格式如下:

chan T // 可以被用来发送和接收类型T的值
chan<- T // 只能被用来发送浮点数
<-chan T // 只能被用来接收整数

其中<-操作符指定信道的方向,发送或接收。若没有给定方向,那么该信道就是双向的。信道可通过类型转换或赋值被强制为只发送或只接收。

信道的初始化可以通过 make 函数来实现,其结果值充当了对底层数据结构的引用。初始化时可以为信道设置缓冲区大小,默认值是零,表示不带缓冲的或同步的信道。

ci := make(chan int) // 整数类型的无缓冲信道
cj := make(chan int, 0) // 整数类型的无缓冲信道
cp := make(chan *os.File, 100) // 指向文件指针的带缓冲信道

3.2 const

const 用于定义常量,一旦创建,不可赋值修改。const可以出现在任何关键字 var 可以出现的地方,声明常量方式与 var 声明变量方式相同,格式如下:

const name = value
const name T = value
const (

name = value
name T = value

)

注意,Golang 中的 const 不支持像 C/C++ 中修饰函数的参数和返回值,即下面的语句是非法的。

func test(const name *string)
func test(name string) const string

3.3 func

func 用于定义函数。Go函数支持变参且返回值支持多个,但不支持默认参数。如果函数存在多个返回值形参则需要使用小括号括起来,定义格式如下:

func funcName(){} //无参无返回值
func funcName(t T) T {} //有参有返回值
func funcName(t T, list ...T) (T1,T1) {} //有变参有多个返回值

代码格式上需要注意的是,函数体的第一个大括号必须函数名同行。这个是Go对代码格式的强制要求,在其它的语句中也是如此,比如if else语句、for语句、switch语句、select语句等。

3.4 interface

interface 用于定义接口。一个接口是一个方法集,如果一个类型实现了一个接口中的所有方法集,那么说明该类型实现此接口。接口类型变量可以存储任何实现了该接口的类型的值。特别的,interface{}表示空接口类型,默认地,所有类型均实现了空接口,所以interface{}可以接收任意类型值。示例如下:

//空接口
interface{}

//一个简单的File接口
type File interface {

Read(b Buffer) bool
Write(b Buffer) bool
Close()

}

3.5 map

map 用于声明映射变量。映射属容器类类型,是一个同种类型元素的无序组,通过唯一的键可以获取对应的值。可以使用 make 创建 map 变量,未初始化的映射值为 nil。创建map变量主要有如下几种方式:

//创建0容量的map
var myMap = make(map[T1] T2)

//创建指定容量的map
var myMap = make(map[T]T2, cap)

//创建并初始化map
var myMap = map[string]int {

"dable" : 27,
"cat" : 28,

}

使用示例:

package main

import "fmt"

func main() {

    nameAge := make(map\[string\]int)
    nameAge\["bob"\] = 18                     //增
    nameAge\["tom"\] = 16                     //增
    delete(nameAge, "bob")                  //删
    nameAge\["tom"\] = 19                     //改
    v := nameAge\["tom"\]                     //查
    fmt.Println("v=",v)
    v, ok := nameAge\["tom"\]                 //查,推荐用法
    if ok { 
        fmt.Println("v=",v,"ok=",ok)
    }   
    for k, v :=range nameAge {              //遍历
            fmt.Println(k, v)
    }   

}

输出结果:

v= 19
v= 19 ok= true
tom 19

3.6 struct

struct 用于定义结构体。结构体属容器类类型,是多个相同或不同类型值的集合。

package main

import "fmt"

type Vertex struct {

X, Y int

}

var (

v1 = Vertex{1, 2}  // 类型为 Vertex
v2 = Vertex{X: 1}  // Y:0 被省略
v3 = Vertex{}      // X:0 和 Y:0
p  = &Vertex{1, 2} // 类型为 *Vertex

)

func main() {

fmt.Printf("%#v %#v %#v %#v\\n", v1, v2, v3, p)

}

输出结果:

main.Vertex{X:1, Y:2} main.Vertex{X:1, Y:0} main.Vertex{X:0, Y:0} &main.Vertex{X:1, Y:2}

3.7 type

type 用于定义类型,比如定义struct、interface与等价类型。

//定义struct
type Person struct { name string }

//定义接口
type Person interface {

speak(word string)

}

//定义等价类型,rune等价于int32
type rune int32

3.8 var

var 用于定义变量,语法格式主要有:

var name T //name默认为类型T的零值
var name = value //根据值value推断变量name的类型
var name T = value //赋初始值时指明类型
var name1, name2 T //同时定义多个同类型变量

//同时定义多个不同类型的变量
var (

name string ="dable"
age int = 18

)

定义变量可以使用:=来替代var,但是:=运算符只能用于函数体内。

4.程序流程控制

4.1 for range break continue

(1)for 与 range for是Go中唯一用于循环结构的关键词。有三个使用方式,分别是单个循环条件,经典的初始化/条件/后续形式,还有和range关键词结合使用来遍历容器类对象(数组、切片、映射)。

//单条件
i := 1
for i <= 3 {

fmt.Println(i)
i = i + 1

}

//初始化/条件/后续形式
//注意Go中没有前置自增与自减运算符,即++i是非法的
for i:=0; i < 3; i++ {

fmt.Println(i)

}

//for range形式遍历数组
array :=[...]int{0,1,2,3,4,5}
for i, v :=range array{

fmt.Println(i,v)

}

(2)break break用于终止最内层的"for"、“switch"或"select"语句的执行。break可以携带标签,用于跳出多层。如果存在标签,则标签必须放在"for”、"switch"或"select"语句开始处。

//终止for
L:

for i < n {
    switch i {
    case 5:
        break L
    }
}

(3)continue continue通常用于结束当前循环,提前进入下一轮循环。也可以像break一样携带标签,此时程序的执行流跳转到标签的指定位置,可用于跳出多层"for"、“switch"或"select”,提前进入下一轮的执行。示例如下:

//提前进入下一轮循环
for i:=0; i < 3; i++ {

if i == 1 {
    continue
}
fmt.Println(i)

}
//输出结果
0
2

//提前进入标签处for的下一轮循环
L:
for i:=0; i < 2; i++ {

for j:=0; j < 3; j++{
    if j == 1 {
        continue L
    }
    fmt.Println(i, j)
}

}
//输出结果
0 0
1 0

4.2 goto

goto用于将程序的执行转移到与其标签相应的语句。可以使用goto退出多层"for"、“switch"或"select”,功能类似于break携带标签。

//终止for
L:

for i < n {
    switch i {
    case 5:
        goto L
    }
}

注意事项: (1)执行"goto"不能在跳转过程中跳过变量的定义,不然会报编译错误。例如:

goto L  //编译报错
v := 3

L:

fmt.Println(v)

(2)在块外的goto语句不能跳转至该块中的标签。例如:

if n%2 == 1 {

goto L1

}
for n > 0 {

f()
n--

L1:

f()
n--

}

是错误的,因为标签 L1 在"for"语句的块中而 goto 则不在。 (3)程序设计时,应尽量避免使用goto语句,因为程序执行流的随意跳转会破坏结构化设计风格,导致代码可读性下降。

4.3 switch case default fallthrough

这四个关键词是结合使用的。switch语句提供多路执行,表达式或类型说明符与switch中的case相比较从而决定执行哪一分支。default用于给出默认分支,即所有的case分支都不满足时执行default分支。Go中的switch语句在执行完某个case子句后,不会再顺序地执行后面的case子句,而是结束当前switch语句。使用fallthrough可以继续执行后面的case与default子句。

下面分别以表达式选择或类型选择为例演示switch case default fallthrough的用法。

//表达式选择

switch tag {
default: s3() //default子句可以出现在switch语句中的任意位置,不一定是最后一个
case 0, 1, 2, 3: s1() //case表达式可以提供多个待匹配的值,使用逗号分隔
case 4, 5, 6, 7: s2()
}

switch x := f(); {
case x < 0: return -x //case表达式无需为常量
default: return x
}

switch { //缺失的switch表达式意为"true"
case x < y: f1()
fallthrough //强制执行下一个case子句
case x < z: f2()
//此处没有fallthrough,switch执行流在此终止
case x == 4: f3()
}

//类型选择
switch i := x.(type) {
case int:

printInt(i)                            // i 的类型为 int

case float64:

printFloat64(i)                        // i 的类型为 float64

case func(int) float64:

printFunction(i)                       // i 的类型为 func(int) float64

case bool, string:

printString("type is bool or string")  // i 的类型为 bool or string

default:

printString("don't know the type")     // i 的类型未知

}

4.4 if else

if与else实现条件控制,与C有许多相似之处,但也有其不同之处。变化主要有三点: (1)可省略条件表达式的括号; (2)支持初始化语句,可定义代码块局部变量; (3)if与else块中只有一条语句也需要添加大括号; (4)起始大括号必须与if与else同行。

if err := file.Chmod(0664); err != nil {

log.Print(err)
return err

}

4.5 return defer

(1)return return用于函数执行的终止并可选地提供一个或多个返回值。 任何在函数F中被推迟的函数会在F 返回给其调用者前执行。函数可以通过return返回多个值。如果返回值在函数返回形参中指定了名字,那么return时可不带返回值列表。

//无返回值
func noResult() {

return

}

//单返回值
func simpleF() int {

return 2

}

//多返回值
func complexF2() (float64, float64) {

re = 7.0
im = 4.0
return re, im

}

//返回值已具名
unc complexF3() (re float64, im float64) {

re = 7.0
im = 4.0
return

}

(2)defer defer语句用于预设一个函数调用,即推迟函数的执行。 该函数会在执行 defer 的函数返回之前立即执行。它显得非比寻常, 但却是处理一些事情的有效方式,例如无论以何种路径返回,都必须释放资源的函数。 典型的例子就是解锁互斥和关闭文件。

//将文件的内容作为字符串返回。
func Contents(filename string) (string, error) {

f, err := os.Open(filename)
if err != nil {
    return "", err
}
defer f.Close()  // f.Close 会在函数结束后运行

var result \[\]byte
buf := make(\[\]byte, 100)
for {
    n, err := f.Read(buf\[0:\])
    result = append(result, buf\[0:n\]...)
    if err != nil {
        if err == io.EOF {
            break
        }
        return "", err  // 我们在这里返回后,f 就会被关闭
    }
}
return string(result), nil // 我们在这里返回后,f 就会被关闭

}

推迟诸如 Close 之类的函数调用有两点好处:第一, 它能确保你不会忘记关闭文件。如果你以后又为该函数添加了新的返回路径时, 这种情况往往就会发生。第二,它意味着“关闭”离“打开”很近, 这总比将它放在函数结尾处要清晰明了。

使用defer时,需要注意两点: (a)被推迟函数的实参(如果该函数为方法则还包括接收者)在推迟执行时就会求值,而不是在调用执行时才求值。这样不仅无需担心变量值在函数执行时被改变, 同时还意味着可以给被推迟的函数传递不同参数。下面是个简单的例子。

for i := 0; i < 5; i++ {

defer fmt.Printf("%d ", i)

}

(b)被推迟的函数按照后进先出(LIFO)的顺序执行,因此以上代码在函数返回时会打印 4 3 2 1 0。

4.6 go

go用于创建Go程(goroutine),实现并发编程。Go程是与其它Go程并发运行在同一地址空间的函数,相比于线程与进程,它是轻量级的。Go程在多线程操作系统上可实现多路复用,因此若一个线程阻塞,比如说等待I/O,那么其它的线程就会运行。Go程的设计隐藏了线程创建和管理的诸多复杂性。

在函数或方法前添加 go 关键字能够在新的Go程中调用它。当调用完成后,该Go程也会安静地退出。效果有点像Unix Shell中的 & 符号,它能让命令在后台运行。

package main

import (

    "fmt"   
    "time"  

)

func main() {

    go func(){ 
        fmt.Println("in first goroutine")
    }()     
    go func(){ 
        fmt.Println("in second goroutine")
    }()     

    fmt.Println("main thread start sleep, and other goroutine start execute")
    time.Sleep(10*time.Second)

}

输出结果:

main thread start sleep, and other goroutine start execute
in second goroutine
in first goroutine

注意,从输出结果可以看出,go程的执行顺序和创建的顺序是没有关系的,也就是说存在多个go程时,其执行的顺序是随机的。

4.7 select

select语句用来选择哪个case中的发送或接收操作可以被立即执行。它类似于switch语句,但是它的case涉及channel有关的I/O操作。也就是说select就是用来监听和channel有关的IO操作,它与select, poll, epoll相似,当IO操作发生时,触发相应的动作,实现IO多路复用。

package main

import "fmt"

func main(){

ch1 := make(chan int, 1)
ch2 := make(chan int, 1)

ch1 <- 3
ch2 <- 5

select {
case <- ch1:
    fmt.Println("ch1 selected.")
case <- ch2:
    fmt.Println("ch2 selected.")
default:
    //如果ch1与ch2没有数据到来,则进入default处理流程。如果没有default子句,则select一直阻塞等待ch1与ch2的数据到来
    fmt.Println("default")
}

}

输出结果:

ch1 selected.

//或者
ch2 selected.

从输出结果可以看出,当存在多个case满足条件,即有多个channel存在数据时,会随机的选择一个执行。

    • *

参考文献

[1]实效Go编程 [2]Go编程语言规范 [3]郝林.Go并发编程实战[M].人民有点出版社.C2.1.2关键字.P16-P17 [4]【golang】select关键字用法


原网址: 访问
创建于: 2021-12-23 15:02:24
目录: default
标签: 无

请先后发表评论
  • 最新评论
  • 总共0条评论