Go语言入门系列二

古人学问无遗力,少壮工夫老始成。这篇文章主要讲述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 = https://www.songbingjia.com/android/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的安全性

延迟调用
【Go语言入门系列二】语句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

    推荐阅读