• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    迪恩网络公众号

Go语言基础语法

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

关键字

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

变量声明

var关键字:(任意情况都可以使用,一般用来定义全局变量)

var vname1, vname2, vname3 type = v1, v2, v3   //type 可省略

简短声明:(仅能用于函数中)

vname1, vname2, vname3 := v1, v2, v3	//编译器自动推导出相应的类型

_(下划线)是个特殊的变量名,任何赋予它的值都会被丢弃。

作用:一般用于接收函数返回得到的后续不使用的值

// 变量较多时,可以分组声明
import(
	"fmt"
	"os"
)

const(
	i = 100
	pi = 3.1415
	prefix = "Go_"
)

var(
	i int
	pi float32
	prefix string
)

基础数据类型

//常量
const cname type = c1	//type可省略
//Boolean
var	active bool = true
//int, int8(byte), int16, int32(rune), int64, uint, uint8, uint16, uint32, uint64
//complex32, complex64
var c complex64 = 5+5i
//string (常量不可修改)
var s string = "hello"
s[0] = 'c'	//编译出错

错误类型

Go内置有一个error类型,专门用来处理错误信息,Go的package里面还专门有一个包errors来处理错误:

err := errors.New("emit macho dwarf: elf header corrupted")
if err != nil {
	fmt.Print(err)
}

iota枚举

默认开始值是0,const中每增加一行加1:

package main

import (
	"fmt"
)

const (
	x = iota // x == 0
	y = iota // y == 1
	z = iota // z == 2
	w        // 常量声明省略值时,默认和之前一个值的字面相同。这里隐式地说w = iota,因此w == 3。其实上面y和z可同样不用"= iota"
)

const v = iota // 每遇到一个const关键字,iota就会重置,此时v == 0

const (
	h, i, j = iota, iota, iota //h=0,i=0,j=0 iota在同一行值相同
)

const (
	a       = iota //a=0
	b       = "B"
	c       = iota             //c=2
	d, e, f = iota, iota, iota //d=3,e=3,f=3
	g       = iota             //g = 4
)

func main() {
	fmt.Println(a, b, c, d, e, f, g, h, i, j, x, y, z, w, v)
}

array

【注意】数组之间赋值为值传递

var arr [n]type	//声明一个长度为n得类型为type的数组
// 声明及初始化
a := [3]int{1, 2, 3} // 声明了一个长度为3的int数组
b := [10]int{1, 2, 3} // 声明了一个长度为10的int数组,其中前三个元素初始化为1、2、3,其它默认为0
c := [...]int{4, 5, 6} // 可以省略长度而采用`...`的方式,Go会自动根据元素个数来计算长度

// 二维数组
// 声明了一个二维数组,该数组以两个数组作为元素,其中每个数组中又有4个int类型的元素
doubleArray := [2][4]int{[4]int{1, 2, 3, 4}, [4]int{5, 6, 7, 8}}
// 上面的声明可以简化,直接忽略内部的类型
easyArray := [2][4]int{{1, 2, 3, 4}, {5, 6, 7, 8}}

slice

【注意】slice为引用类型

// 和声明array一样,只是少了长度
var fslice []int
// 声明及初始化
slice := []byte {'a', 'b', 'c', 'd'}

slice可以从一个数组或一个已经存在的slice中再次声明。slice通过array[i:j]来获取,其中i是数组的开始位置,j是结束位置,但不包含array[j],它的长度是j-i

// 声明一个含有10个元素类型为byte的数组
var ar = [10]byte {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}

// 声明两个含有byte的slice
var a, b []byte

// a指向数组的第3个元素开始,并到第五个元素结束,
a = ar[2:5]
//现在a含有的元素: ar[2]、ar[3]和ar[4]

// b是数组ar的另一个slice
b = ar[3:5]
// b的元素是:ar[3]和ar[4]

注意slice和数组在声明时的区别:声明数组时,方括号内写明了数组的长度或使用...自动计算长度,而声明slice时,方括号内没有任何字符。

slice是引用类型,所以当引用改变其中元素的值时,其它的所有引用都会改变该值。

对于slice有几个有用的内置函数:

  • len 获取slice的长度
  • cap 获取slice的最大容量
  • appendslice里面追加一个或者多个元素,然后返回一个增加元素后的slice
  • copy 函数从源slicesrc中复制元素到目标dst,并且返回复制的元素的个数

注:append函数会改变slice所引用的数组的内容,从而影响到引用同一数组的其它slice。 但当slice中没有剩余空间(即(cap-len) == 0)时,此时将动态分配新的数组空间。返回的slice数组指针将指向这个空间,而原数组的内容将保持不变;其它引用此数组的slice则不受影响。

package main

import "fmt"

func main() {

	array := [...]int{1,2,3,4,5,6,7}

	s := array[2:4]
	a := append(s, 6)
	
	fmt.Println(array, s, a)
}
// 输出结果
// PS E:\mygo\src> go run slice.go
// [1 2 3 4 6 6 7] [3 4] [3 4 6]

从Go1.2开始slice支持了三个参数的slice,之前我们一直采用这种方式在slice或者array基础上来获取一个slice

var array [10]int
slice := array[2:4]

这个例子里面slice的容量是8,新版本里面可以指定这个容量

slice = array[2:4:7]

上面这个的容量就是7-2,即5。这样这个产生的新的slice就没办法访问最后的三个元素。

如果slice是这样的形式array[:i:j],即第一个参数为空,默认值就是0。

map

【注意】map也是引用类型

map也就是Python中字典的概念,它的格式为map[keyType]valueType

// 声明一个key是字符串,值为int的字典,这种方式的声明需要在使用之前使用make初始化
var numbers map[string]int
// 另一种map的声明方式
numbers = make(map[string]int)
numbers["one"] = 1  //赋值
numbers["ten"] = 10 //赋值
numbers["three"] = 3

fmt.Println("第三个数字是: ", numbers["three"]) // 读取数据
// 打印出来如:第三个数字是: 3

通过delete删除map的元素:

// 初始化一个字典
rating := map[string]float32{"C":5, "Go":4.5, "Python":4.5, "C++":2 }
// map有两个返回值,第二个返回值,如果不存在key,那么ok为false,如果存在ok为true
csharpRating, ok := rating["C#"]
if ok {
	fmt.Println("C# is in the map and its rating is ", csharpRating)
} else {
	fmt.Println("We have no rating associated with C# in the map")
}

delete(rating, "C")  // 删除key为C的元素

上面说过了,map也是一种引用类型,如果两个map同时指向一个底层,那么一个改变,另一个也相应的改变:

m := make(map[string]string)
m["Hello"] = "Bonjour"
m1 := m
m1["Hello"] = "Salut"  // 现在m["hello"]的值已经是Salut了

make、new操作

make用于内建类型(mapslicechannel)的内存分配。new用于各种类型的内存分配。

内建函数new本质上说跟其它语言中的同名函数功能一样:new(T)分配了零值填充的T类型的内存空间,并且返回其地址,即一个*T类型的值。用Go的术语说,它返回了一个指针,指向新分配的类型T的零值。有一点非常重要:

new返回指针。

内建函数make(T, args)new(T)有着不同的功能,make只能创建slicemapchannel,并且返回一个有初始值(非零)的T类型,而不是*Targs包括了len长度和cap容量。本质来讲,导致这三个类型有所不同的原因是指向数据结构的引用在使用前必须被初始化。例如,一个slice,是一个包含指向数据(内部array)的指针、长度和容量的三项描述符;在这些项目被初始化之前,slicenil。对于slicemapchannel来说,make初始化了内部的数据结构,填充适当的值。

make返回初始化后的(非零)值。

区别:

package main

import "fmt"

func main() {
	var p *[]int = new([]int)	//声明一个指向slice的指针

	(*p)[0] = 1
	fmt.Println((*p)[0], (*p)[1])

	var s []int = make([]int, 3)	//使用make来初始化slice,len=3
	s[0] = 2
	fmt.Println(s[0], s[1])
}
// 输出结果
// PS E:\mygo\src> go run slice.go
// panic: runtime error: index out of range

// goroutine 1 [running]:
// main.main()
//        E:/mygo/src/slice.go:16 +0x11
// exit status 2

原因:

​ 利用new来分配slice结构,但是结构中的应该指向底层数组的ptr指针为空,故不能向这个slice里面存取数据;利用make来分配slice结构,此时结构中应该只想底层数组的ptr指针已经指向了某个底层数组。这个底层数组已经分配了,所以可以使用。

func

func funcName(input1 type1, input2 type2) (output1 type1, output2 type2) {
	//这里是处理逻辑代码
	//返回多个值
	return value1, value2
}

变参

func myfunc(arg ...int) {}
// arg ...int告诉Go这个函数接受不定数量的参数。注意,这些参数的类型全部是int。在函数体中,变量arg是一个int的slice

函数作为参数

package main

import "fmt"

type testInt func(int) bool // 声明了一个函数类型

func isOdd(integer int) bool {
	if integer%2 == 0 {
		return false
	}
	return true
}

func isEven(integer int) bool {
	if integer%2 == 0 {
		return true
	}
	return false
}

// 声明的函数类型在这个地方当做了一个参数

func filter(slice []int, f testInt) []int {
	var result []int
	for _, value := range slice {
		if f(value) {
			result = append(result, value)
		}
	}
	return result
}

func main(){
	slice := []int {1, 2, 3, 4, 5, 7}
	fmt.Println("slice = ", slice)
	odd := filter(slice, isOdd)    // 函数当做值来传递了
	fmt.Println("Odd elements of slice are: ", odd)
	even := filter(slice, isEven)  // 函数当做值来传递了
	fmt.Println("Even elements of slice are: ", even)
}

函数内部常用语法:defer,panic,recover

defer–延迟语句

可以在函数中添加多个defer语句,当函数执行到最后时,这些defer语句会按照逆序执行,最后该函数返回。 (有些类似于析构函数)

例子:

func ReadWrite() bool {
	file.Open("file")
// 做一些工作
	if failureX {
		file.Close()
		return false
	}

	if failureY {
		file.Close()
		return false
	}

	file.Close()
	return true
}

我们看到上面有很多重复的代码,Go的defer有效解决了这个问题。使用它后,不但代码量减少了很多,而且程序变得更优雅。在defer后指定的函数会在函数退出前调用。

func ReadWrite() bool {
	file.Open("file")
	defer file.Close()
	if failureX {
		return false
	}
	if failureY {
		return false
	}
	return true
}
panic

是一个内建函数,可以中断原有的控制流程,进入一个panic状态中。当函数F调用panic,函数F的执行被中断,但是F中的延迟函数会正常执行,然后F返回到调用它的地方。在调用的地方,F的行为就像调用了panic。这一过程继续向上,直到发生panicgoroutine中所有调用的函数返回,此时程序退出。panic可以直接调用panic产生。也可以由运行时错误产生,例如访问越界的数组。

下面这个函数演示了如何在过程中使用panic

var user = os.Getenv("USER")

func init() {
	if user == "" {
		panic("no value for $USER")
	}
}
recover

是一个内建的函数,可以让进入panic状态的goroutine恢复过来。recover仅在延迟函数中有效。在正常的执行过程中,调用recover会返回nil,并且没有其它任何效果。如果当前的goroutine陷入panic状态,调用recover可以捕获到panic的输入值,并且恢复正常的执行。

下面这个函数检查作为其参数的函数在执行时是否会产生panic

func throwsPanic(f func()) (b bool) {
	defer func() {
		if x := recover(); x != nil {
			b = true
		}
	}()
	f() //执行函数f,如果f中出现了panic,那么就可以恢复回来
	return
}

main函数和init函数

init函数(能够应用于所有的package)和main函数(只能应用于package main)。这两个函数在定义时不能有任何的参数和返回值

import

fmt是Go语言的标准库,其实是去GOROOT环境变量指定目录下去加载该模块,当然Go的import还支持如下两种方式来加载自己写的模块:

  1. 相对路径

    import “./model” //当前文件同一目录的model目录,但是不建议这种方式来import

  2. 绝对路径

    import “shorturl/model” //加载gopath/src/shorturl/model模块

上面展示了一些import常用的几种方式,但是还有一些特殊的import,让很多新手很费解,下面我们来一一讲解一下到底是怎么一回事

  1. 点操作

    我们有时候会看到如下的方式导入包

     import(
         . "fmt"
     )
    

    这个点操作的含义就是这个包导入之后在你调用这个包的函数时,你可以省略前缀的包名,也就是前面你调用的fmt.Println(“hello world”)可以省略的写成Println(“hello world”)

  2. 别名操作

    别名操作顾名思义我们可以把包命名成另一个我们用起来容易记忆的名字

     import(
         f "fmt"
     )
    

    别名操作的话调用包函数时前缀变成了我们的前缀,即f.Println(“hello world”)

  3. _操作

    这个操作经常是让很多人费解的一个操作符,请看下面这个import

      import (
    	  "database/sql"
          _ "github.com/ziutek/mymysql/godrv"
      )
    

    _操作其实是引入该包,而不直接使用包里面的函数,而是调用了该包里面的init函数。


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
Go 其四 函数发布时间:2022-07-10
下一篇:
Go基础篇【第2篇】: 内置库模块 fmt发布时间:2022-07-10
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap