Go最全(四)Go-----运算操作,流程控制(if,Golang面试总结

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

fmt.Println(i)

i++

}

方式四:字符串,数组,切片,映射,管道

遍历一个可迭代对象

for range


range 后可接数组、切片,字符串等

  • range 会复制对象

返回两个值:索引和数据,若你后面的代码用不到索引,需要使用 _ 表示 。

name :=“长得帅”

//索引 i , 字符ch name字符串

for i,ch := range name{

fmt.Println(i,ch)

}

占位符 “_”

name :=“长得帅”

//索引 i , 字符ch name字符串

for _,ch := range name{

fmt.Println(ch)

}

在这里插入图片描述

在这里插入图片描述

  • 建议用引用类型底层数据不会被复制

package main

func main() {

s := []int{1, 2, 3, 4, 5}

for i, v := range s { // 复制 struct slice { pointer, len, cap }。

if i == 0 {

s = s[:3] // 对 slice 的修改,不会影响 range。

s[2] = 100 // 对底层数据的修改。

}

println(i, v)

}

}

//结果

0 1

1 2

2 100

3 4

4 5

4. 结束语句

===========================================================================

  1. continue

跳过本次循环

  1. break

break lable 跳出循环,可以如下,跳出多层循环

END

for i :=1 ;i<=100 ;i++{

break END

}

  1. return用在方法或函数中,表示终止所在的方法或函数(method与function)

return在main函数中,表示终止main函数,终止程序

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

if i == 5 {

return //直接退出main函数了

}

fmt.Printf(“本次循环次数:%d\n”, i)

}

//永远走不带这里了,第五次for循环时候,直接return了

fmt.Println(“循环结束,走到了我”)

5. 跳转goto

=============================================================================

goto

标签:大写的英文字母,放在最左面

可以一次性跳出多层循环

goto 标签A

标签A:

在这里插入图片描述

用跳转实现for循环

在这里插入图片描述

goto语句与标签之间不能有变量声明,否则编译错误

.\main.go:7:7: goto flag jumps over declaration of say at .\main.go:8:6

6. defer 延迟语句

=================================================================================

1. 延迟调用


在后面跟一个函数的调用,就能实现将这个 xxx 函数的调用延迟到当前函数执行完后再执行

defer xxx()

import “fmt”

func myfunc() {

fmt.Println(“B”)

}

func main() {

defer myfunc()

fmt.Println(“A”)

}

//输出如下

A

B

import “fmt”

func main() {

defer fmt.Println(“B”)

fmt.Println(“A”)

}

2. 即时求值的变量快照


使用 defer 只是延时调用函数,此时传递给函数里的变量,不应该受到后续程序的影响

比如这边的例子

import “fmt”

func main() {

name := “go”

defer fmt.Println(name) // 输出: go

name = “python”

fmt.Println(name) // 输出: python

}

// 输出

python

go

可见给 name 重新赋值为 python,后续调用 defer 的时候,仍然使用未重新赋值的变量值,就好在 defer 这里,给所有的这是做了一个快照一样。

如果 defer 后面跟的是匿名函数,情况会有所不同, defer 会取到最后的变量值

package main

import “fmt”

func main() {

name := “go”

defer func(){

fmt.Println(name) // 输出: python

}()

name = “python”

fmt.Println(name) // 输出: python

}

3. 多个defer 反序调用


当我们在一个函数里使用了 多个defer,那么这些defer 的执行函数是如何的呢?

import “fmt”

func main() {

name := “go”

defer fmt.Println(name) // 输出: go

name = “python”

defer fmt.Println(name) // 输出: python

name = “java”

fmt.Println(name) //输出:java

}

//输出

java

python

go

输出如下,可见 多个defer 是反序调用的,有点类似一样,后进先出。

4. defer 与 return 先后顺序


defer 和 return 到底是哪个先调用?

import “fmt”

var name string = “go”

func myfunc() string {

defer func() {

name = “python”

}()

fmt.Printf(“myfunc 函数里的name:%s\n”, name)

return name

}

func main() {

myname := myfunc()

fmt.Printf(“main 函数里的name: %s\n”, name)

fmt.Println("main 函数里的myname: ", myname)

}

//输出

myfunc 函数里的name:go

main 函数里的name: python

main 函数里的myname: go

第一行name 此时还是全局变量,值还是go

第二行也不难理解,在 defer 里改变了这个全局变量,此时name的值已经变成了 python

第三行, defer 是return 后才调用的。所以在执行 defer 前,myname 已经被赋值成 go 了。

5. 为什么要有 defer?


把这个放在 defer 执行的函数放在 return 那里执行不就好了。

但是当一个函数里有多个 return 时,你得多调用好多次这个函数,代码就臃肿起来了。

没有 defer

func f() {

r := getResource() //0,获取资源

if … {

r.release() //1,释放资源

return

}

if … {

r.release() //2,释放资源

return

}

if … {

r.release() //3,释放资源

return

}

r.release() //4,释放资源

return

}

用了 defer 后,代码就显得简单直接,不管你在何处 return,都会执行 defer 后的函数

func f() {

r := getResource() //0,获取资源

defer r.release() //1,释放资源

if … {

return

}

if … {

return

}

if … {

return

}

return

}

7. 异常机制:panic 和 recover

===========================================================================================

  • panic:出异常,使程序崩溃

  • recover:捕获异常,恢复程序或做收尾工作

panic、recover 参数类型为 interface{},因此可抛出任何类型对象。

revocer 调用后,抛出的 panic 将会在此处终结,不会再外抛,但是 recover,并不能任意使用,它有强制要求,必须得在 defer 下才能发挥用途。

  1. 利用recover处理panic指令,defer 必须放在 panic 之前定义,另外 recover 只有在 defer 调用的函数中才有效。否则当panic时,recover无法捕获到panic,无法防止panic扩散。

  2. recover 处理异常后,逻辑并不会恢复到 panic 那个点去,函数跑到 defer 之后的那个点。

  3. 多个 defer 会形成 defer 栈,后定义的 defer 语句会被最先调用。

1. 触发panic


手动触发宕机,是非常简单的一件事,只需要调用 panic 这个内置函数

package main

func main() {

panic(“crash”)

}

运行后,直接报错宕机

$ go run main.go

go run main.go

panic: crash

goroutine 1 [running]:

main.main()

E:/Go-Code/main.go:4 +0x40

exit status 2

在这里插入图片描述

2. 捕获 panic用recover


发生了异常,有时候就得捕获

这就不得不引出另外一个内建函数 – recover,它可以让程序在发生宕机后起生回生

但是 recover 的使用,有一个条件,就是它必须在 defer 函数中才能生效,其他作用域下,它是不工作的。

import “fmt”

func set_data(x int) {

defer func() {

// recover() 可以将捕获到的panic信息打印

if err := recover(); err != nil {

fmt.Println(err)

}

}()

// 故意制造数组越界,触发 panic

var arr [10]int

arr[x] = 88

}

func main() {

set_data(20)

// 如果能执行到这句,说明panic被捕获了

// 后续的程序能继续运行

fmt.Println(“everything is ok”)

}

运行后

$ go run main.go

runtime error: index out of range [20] with length 10

everything is ok

通常来说,不应该对进入 panic 宕机的程序做任何处理,但有时,需要我们可以从宕机中恢复,至少我们可以在程序崩溃前,做一些操作,

当 web 服务器遇到不可预料的严重问题时,在崩溃前应该将所有的连接关闭,如果不做任何处理,会使得客户端一直处于等待状态,如果 web 服务器还在开发阶段,服务器甚至可以将异常信息反馈到客户端,帮助调试。

3. 无法跨协程


即使 panic 会导致整个程序退出,但在退出前,若有 defer 延迟函数,还是得执行完 defer

但是这个 defer 在多个协程之间是没有效果,在子协程里触发 panic只能触发自己协程内的 defer调用 main 协程里的 defer 函数的。

import (

“fmt”

“time”

)

func main() {

// 这个 defer 并不会执行

defer fmt.Println(“in main”)

go func() {

defer println(“in goroutine”)

panic(“”)

}()

time.Sleep(2 * time.Second)

}

//输出如下

in goroutine

panic:

goroutine 6 [running]:

main.main.func1()

E:/Go-Code/main.go:12 +0x7b

created by main.main

E:/Go-Code/main.go:10 +0xbc

exit status 2

向已关闭的通道发送数据会引发panic

package main

import (

“fmt”

)

func main() {

defer func() {

if err := recover(); err != nil {

fmt.Println(err)

}

}()

var ch chan int = make(chan int, 10)

close(ch)

ch <- 1

}

//结果

send on closed channel

练习

=====================================================================

将一个数从个位,十位,百位输出

思路

百位: n/100%10

十位: n/10%10

个位: n/1%10

代码

package main

import “fmt”

func main() {

var n,s int

fmt.Scan(&n)

if n>=999 {

s =4

} else if n>99{

s =3

}else if n>9{

s=2

}else {

s=1

}

for i:=1;i<=s;i++{

// l为10 *i

l :=1

for j:=2;j<=i;j++{

l *=10

}

fmt.Println(n/l%10)

}

}

多次登陆

package main

import “fmt”

func main() {

var name,pwd string

//限制:只能重复登录三次,

var logincache=3

for i :=1;i<=3;i++{

fmt.Println(“请输入用户名”)

fmt.Scanln(&name)

fmt.Println(“请输入密码”)

fmt.Scanln(&pwd)

if name == “qcq” && pwd == “qcq123” {

fmt.Println(“欢迎来着王者荣耀!”)

break

}else {

logincache–

fmt.Printf(“输入错误,你还有%d次机会\n”,logincache)

}

}

}

输出一个正方行

计算100以内所有奇数的和

func main() {

i :=1

num :=0

for i<=100{

num +=i

i+=2

}

fmt.Println(num)

}

给学生成绩评级

func main() {

var n int

fmt.Scanln(&n)

img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

} else if n>99{

s =3

}else if n>9{

s=2

}else {

s=1

}

for i:=1;i<=s;i++{

// l为10 *i

l :=1

for j:=2;j<=i;j++{

l *=10

}

fmt.Println(n/l%10)

}

}

多次登陆

package main

import “fmt”

func main() {

var name,pwd string

//限制:只能重复登录三次,

var logincache=3

for i :=1;i<=3;i++{

fmt.Println(“请输入用户名”)

fmt.Scanln(&name)

fmt.Println(“请输入密码”)

fmt.Scanln(&pwd)

if name == “qcq” && pwd == “qcq123” {

fmt.Println(“欢迎来着王者荣耀!”)

break

}else {

logincache–

fmt.Printf(“输入错误,你还有%d次机会\n”,logincache)

}

}

}

输出一个正方行

计算100以内所有奇数的和

func main() {

i :=1

num :=0

for i<=100{

num +=i

i+=2

}

fmt.Println(num)

}

给学生成绩评级

func main() {

var n int

fmt.Scanln(&n)

[外链图片转存中…(img-7nhBHuc1-1715512624573)]
[外链图片转存中…(img-QK24ZpG6-1715512624574)]
[外链图片转存中…(img-l2ezIkVE-1715512624574)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

  • 15
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值