Go语言入门系列二

Posted 小杰编程

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Go语言入门系列二相关的知识,希望对你有一定的参考价值。

接上篇


运算符优先级

高   *   /  %   <<  >>  &  &^
+ - | ^
== != < <= > >=
&&
||

二元运算符中,除了位移操作符外,操作数类型必须相同。如果其中一个是无显示类型声明的常量,那么该常量操作数会自动转型。

func main() 
const v = 20; //无显式类型声明的常量
var a byte = 10;
b := v + a
fmt.Printf("%T,%v\\n",b,b) //v 自动转换为 byte/uint8类型

自增、自减不再是运算符,而是独立的语句

指针

指针类型与C语言中大体相同,但是多了一些限制:

  1. 不支持加减法运算和类型转换
  2. 指针没有专门的 -> 符合,而是都使用. 选择表达式
  3. 零长度对象的地址是否相等和具体实现有关但是一定不等于nil

复合类型初始化

Go语言在C语言的基础上加了一些限制,舍弃了C语言的灵活,但是换来的是整齐统一的书写形式和规范。

限制:

  1. 初始化表达式必须包含类型标签
  2. 左花括号必须在类型尾部,不能另起一行
  3. 多个成员初始值以逗号分隔
  4. 允许多行,但每行须以逗号或右花括号结束
type data struct
x int
s string

var a data = data 1, "abc"
b := data
1,
"abc",

基本语法区别

if...else..
  1. 条件表达式必须是布尔类型,左花括号不能另起一行
  2. 可定义块局部变量或执行初始化函数
func main()
x := 3

if xinit();x > 5 //可以这儿执行初始化函数或定义局部变量
println("a")
else if x < 5 && x > 0
println("b")
else


switch
  1. switch后面也不需要加括号
  2. case 中不需要加break,会自动加
  3. 想要执行下一个case必须在当前case中添加fallthrough
  4. 都不匹配会执行default与其顺序无关
func main()
switch x := 5;x
case 5:
x += 10
fallthrough //继续向下执行
case 6:
x += 20 //在此处加fallthrough 会报错
default:
pirntln(x)


for
  1. 可使用for...range完成数据迭代
  2. 允许返回单值,使用_来忽略
  3. 定义的局部变量会重用地址(对闭包有影响)
  4. range会复制目标数据,受影响的是数组,可改用数组指针或切片类型
func main()
data :=[3]int[10,20,30]

for i, x := range data //i是索引,x是值
if i == 0
data[0] += 100
data[1] += 200
data[2] += 300


for i, _ := range data //忽略值
if i == 0
data[0] += 100
data[1] += 200
data[2] += 300


函数

使用关键字func定义函数,限制:

  1. 不支持命名嵌套定义
  2. 不支持同名函数重载
  3. 不支持默认参数

灵活点:

  1. 无须前置声明
  2. 支持不定变参
  3. 支持多返回值
  4. 支持命名返回值
  5. 支持匿名函数和闭包

函数签名:这里函数签名指的是参数和返回值构成的一对,都相同才是同一类型

func test()   //左花括号不能另起一行


func test1(f func()) //左花括号不能另起一行
f() //可以传入函数作为参数,然后直接调用
if( f == nil) //函数只能用来判断是否为nil,不支持其他比较方式

type FormatFunc func (string, ...interface)(string,error) //定义函数类型更方便

Go支持返回局部变量指针,并且是安全的

函数参数

Go不支持有默认值的可选参数,不支持命名实参,调用时必须按照签名顺序传递指定类型和数量的实参,就算以"_"命名的参数也不能忽略

不管是指针、引用类型、还是其他类型参数,都是值拷贝传递,区别无非是拷贝目标对象还是拷贝指针

func test(x *int)
fmt.Printf("pointer: %p,target: %v\\n",&x, x)

func main()
a := 0x100
p := &a
fmt.Printf("pointer: %p,target: %v\\n", &p, p)

可见依然把指针做了一次拷贝,两个指针内存是不一样的,但是指向的是相同的

变参

变参本质上就是切片,只能接受同类型的,并且只能放在末尾。因为是切片所以可以修改源数据,如果想拷贝底层数据需要调用copy函数

func test(a...int)
fmt.Println(a)

func main()
a := [3]int1,2,3
test(a[:]...) //转换为slice后展开 注意需要加三个点

返回值

  1. 有返回值的函数必须有return,除非有panic或Z者无break的死循环
  2. 可以用"_"忽略不想要的返回值,返回值可用作其他调用参数或当作结果返回
  3. 命名返回值可以隐式返回
  4. 命名时我们必须对全部返回值命名,否则会编译错误
func div(x,y int) (z int, err error)
if y == 0
err = errors.New("division by zero")
return //默认返回 0 和 "division by zero"

z = x/y
return //返回 z 和 nil

返回值类型就能看出用意的时候尽量不要采用命名返回值

匿名函数

即没有定义名字符合的函数,但是其可以在函数内部定义,形成类似嵌套效果。还可直接调用,保存到变量作为参数或返回值。

func main()
add := func(x, y int) int
return x + y

printfln(add(1, 2))

匿名函数优势:

  1. 除闭包因素外,匿名函数是常见的重构手段
  2. 可将大函数分解成多个相对独立的匿名函数块,实现框架的细节分离
  3. 匿名函数的作用域隔离,更加灵活
  4. 没有应以顺序限制,必要时可抽离

闭包

闭包指的是可以包含代码块中未定义但是本身在其上下文中 并没有被销毁的自由变量

闭包的价值在于可以作为函数对象或者也匿名函数,对于类型系统而言,这不仅以为这数据还要表示代码。支持闭包的多数语言都将函数作为第一级数对象,就是说这些函数可以存储到被闭包引用的变量会一直存在Go语言中的闭包同样也会引用到函数外的变量。闭包的实现却表里只要闭包还被使用,那么帮你报被引用的变量就会一直存在

package main
import
"fmt"

func main()
var j int = 5;
a := func()(func())
var i int = 10
return func()
fmt.Printf("i , j: %d, %d\\n", i ,j)

()

a()

j *= 2

a()


//i , j : 10 , 5
//i , j : 10 , 10

//变量a指向的闭包函数引用了局部变量i和j,i的指被隔离,在闭包外不能被修改,改变j的值以后,再次调用a,发现结果是修改过的值
//在变量a指向的闭包函数中,只有内部的匿名函数才能访问变量i,而无法通过其他途径访问到,因此保证了i的安全性

延迟调用

语句defer向当前函数注册稍后执行的函数调用。

一个函数中可以存在多个defer语句,因此需要注意的是defer语句的调用是先进后出的原则,即最后一个defer语句将最先被执行。

只是注册,不论在函数的什么位置,直到函数结束前才执行,常用于资源释放、解除锁定、错误处理等

func main()
f,err := os.Open("./main.go")
if err != nil
log.Fatalln(err)


defer f.Close() //最后调用


... //其他事


func main()
x, y := 1, 2
defer func(a int)
println("defer x, y = ", a, y) //y 为闭包引用
(x) //注册时复制调用参数

x += 100 //对 x 的修改不会影响延迟调用
y += 200
println(x, y)


//输出:
//101 202
//defer x, y = 1 102
Golang系列之快速入门教程

Go语言入门教程系列——函数循环与分支

Go语言入门教程系列——函数循环与分支

Go急速入门系列

GO系列 | 5分钟入门GO译

《Golang 入门系列一》安装和IDE介绍