Golang教程二(判断,循环语句,函数,指针,init,defer)

目录

一、判断语句

1.if语句

2.switch语句

二、循环语句

1.传统for循环

2.死循环

3.while模式

4.do-while模式

5.遍历切片

6.遍历map

7.break,continue

三、函数,指针

1.函数定义

2.匿名函数

3.高阶函数

4.闭包

5.值传递和引用传递

6.指针

四、init函数和defer函数

1.init函数

2.defer函数

3.defer面试题


一、判断语句

1.if语句

Go 复制代码
package main

import "fmt"

func main() {
	fmt.Println("请输入你的年龄:")
	var age int
	fmt.Scan(&age)

	if age <= 18 {
		if age <= 10 {
			fmt.Println("儿童")
		} else {
			fmt.Println("未成年")
		}
	} else if age <= 35 {
		fmt.Println("青年")
	} else {
		fmt.Println("中老年")
	}
}

2.switch语句

可以理解为case的值就是switch的枚举结果

一般来说,go的switch的多选一,满足其中一个结果之后,就结束switch了

Go 复制代码
package main

import "fmt"

func main() {
	fmt.Println("请输入你的年龄:")
	var age int
	fmt.Scan(&age)

	switch {
	case age <= 0:
		fmt.Println("未出生")
	case age <= 18:
		fmt.Println("未成年")
	case age <= 35:
		fmt.Println("青年")
	default:
		fmt.Println("中年")
	}
}

我输入一个12,我希望它能输出满足的所有条件,例如我希望它输出,未成年,青年

Go 复制代码
package main

import "fmt"

func main() {
	fmt.Println("请输入你的年龄:")
	var age int
	fmt.Scan(&age)

	switch {
	case age <= 0:
		fmt.Println("未出生")
		fallthrough
	case age <= 18:
		fmt.Println("未成年")
		fallthrough
	case age <= 35:
		fmt.Println("青年")
	default:
		fmt.Println("中年")
	}
}

二、循环语句

1.传统for循环

Go 复制代码
package main

import "fmt"

func main() {

	var sum = 0
	for i := 0; i <= 100; i++ {
		sum += i
	}
	fmt.Println(sum)
}

2.死循环

Go 复制代码
package main

import (
	"fmt"
	"time"
)

func main() {
	//每隔1秒打印当前的时间
	for {
		time.Sleep(1 * time.Second)
		fmt.Println(time.Now().Format("2006-01-02 15:04:05")) // 年月日时分秒的固定格式
	}
}

3.while模式

Go 复制代码
package main

import "fmt"

func main() {
	//由于golang没有while循环,如果需要,则是由for循环稍微变化得来
	i := 0
	sum := 0
	for i <= 100 {
		sum += i
		i++
	}
	fmt.Println(sum)
}

4.do-while模式

Go 复制代码
package main

import "fmt"

func main() {
	//do-while模式就是先执行一次循环体,再判断
	i := 0
	sum := 0
	for {
		sum += i
		i++
		if i == 101 {
			break
		}
	}
	fmt.Println(sum)
}

5.遍历切片

Go 复制代码
package main

import "fmt"

func main() {
	// 第一个参数是索引,第二个参数是值
	s := []string{"os", "lee"}
	for index, s2 := range s {
		fmt.Println(index, s2)
	}
}

6.遍历map

Go 复制代码
package main

import "fmt"

func main() {
	//第一个参数就是key,第二个就是value
	s := map[string]int{
		"age":   24,
		"price": 1000,
	}
	for key, val := range s {
		fmt.Println(key, val)
	}
}

7.break,continue

  1. break用于跳出当前循环
  2. continue用于跳过本轮循环

三、函数,指针

函数是一段封装了特定功能的可重用代码块,用于执行特定的任务或计算

函数接受输入(参数)并产生输出(返回值)

1.函数定义

Go 复制代码
package main

import "fmt"

func add(n1 int, n2 int) int {
	fmt.Println(n1, n2)
	return n1 + n2
}

// 参数类型一样,可以合并在一起
func add1(n1, n2 int) int {
	fmt.Println(n1, n2)
	return n1 + n2
}

// 多个参数
func add2(numList ...int) {
	fmt.Println(numList)
}

func main() {
	i := add(1, 2)
	fmt.Println(i)
	i2 := add1(1, 2)
	fmt.Println(i2)
	add2(1, 2, 3, 4)
}

2.匿名函数

匿名函数(Anonymous Function)是一种没有命名的函数定义。

特点

  1. 无名称:匿名函数没有显式的名字,因此不能像普通函数那样通过名字被调用或在其他地方被引用。
  2. 内联定义:可以在需要使用函数的地方直接定义,无需单独的函数声明或定义部分。
  3. 简洁性:特别适合用于一次性使用的简单功能,避免了为一个小功能特意命名一个函数的繁琐。
  4. 可赋值给变量:匿名函数可以赋值给一个变量,通过变量名来调用,此时变量充当了函数的"代理名"。
  5. 可作为参数或返回值:匿名函数可以作为其他函数的参数或返回值,这是高阶函数的基础。
  6. 访问外部变量:匿名函数可以访问其定义时所在作用域内的变量,形成闭包。
Go 复制代码
package main

import "fmt"

func main() {
	var add = func(a, b int) int {
		return a + b
	}
	fmt.Println(add(1, 2))
}

3.高阶函数

高阶函数是指可以接收一个或多个函数作为参数,或者返回一个函数作为结果的函数。这样的函数提升了代码的抽象层次,使得我们能够以更通用和灵活的方式处理函数,从而简化代码并促进复用。

Go 复制代码
package main

import "fmt"

func ApplyToIntList(list []int, f func(int) int) []int {
	result := make([]int, len(list))
	for i, v := range list {
		result[i] = f(v)
	}
	return result
}

func main() {
	// 编写一个高阶函数,接受一个整数列表和一个处理函数作为参数,返回处理函数对该列表所有元素应用后的结果列表。 示例代码:
	squareAll := ApplyToIntList([]int{1, 2, 3, 4}, func(x int) int { return x * x })
	fmt.Println(squareAll) // 输出:[1, 4, 9, 16]
}

4.闭包

闭包是一种特殊的函数,是该函数和其周围环境(即自由变量)组成的绑定关系。当一个函数访问其外部作用域中的变量时,即使在其外部作用域已经结束(函数返回)后,只要闭包还在使用,这些变量仍会保持活跃。闭包常与高阶函数一起使用,作为高阶函数的返回值或参数。

Go 复制代码
package main

import "fmt"

func counter() func() int {
	count := 0
	return func() int {
		count++
		return count
	}
}

func main() {
	// counter高阶函数,func() int是闭包
	counterFunc := counter()
	fmt.Println(counterFunc()) // 输出:1
	fmt.Println(counterFunc()) // 输出:2
	fmt.Println(counterFunc()) // 输出:3

	// 创建另一个计数器实例
	anotherCounter := counter()
	fmt.Println(anotherCounter()) // 输出:1
}

5.值传递和引用传递

  • 值传递:函数接收到的是实参的副本,对形参的修改不影响实参。
  • 引用传递:函数接收到的是实参的引用,对形参的修改直接影响实参。

稍微了解过编程的都应该知道,计算机上显示的所有的数据都是在内存里面的

也就是说,我们定义的一个变量,它也在内存里面有一块地

正常情况下来说,函数的参数是将之前那块地复制了一份出来

如何证明呢

Go 复制代码
package main

import "fmt"

func add(num int) {
	fmt.Println(&num) // 可以看到,这个n的内存地址和外面num的内存地址是明显不一样的
	num = 2           // 这里的修改不会影响外面的num
}

func main() {
	num := 20
	fmt.Println(&num)
	add(num)
	fmt.Println(num) // 20
}

也就是说,在函数里面不管怎么修改这个参数,都不会影响原来的那个值

但是,如果我需要在函数体内修改变量的值呢?

这就需要用到引用传递了

我们直接将变量的内存地址传递进去

Go 复制代码
package main

import "fmt"

func add(num *int) {
	fmt.Println(num) // 内存值是一样的
	*num = 2         // 这里的修改会影响外面的num
}

func main() {
	num := 20
	fmt.Println(&num)
	add(&num)
	fmt.Println(num) // 成功修改 2
}

6.指针

指针是一种特殊的数据类型,它存储的是另一个变量的内存地址。简单来说,指针就是一个变量的"地址标签",通过这个标签,程序可以直接访问和操作该变量在内存中的值。理解指针的关键在于以下几个方面:

  1. 内存地址与值

    • 变量在内存中占据一块特定的空间,每个变量都有一个唯一的内存地址。
    • 变量的值就是存储在这块内存空间中的数据。
  2. 指针变量声明

    • 声明一个指针变量时,使用星号 * 作为前缀来表示这是一个指针类型,并指定它指向的值的类型。
    • 声明格式为 var pointerName *typeName,其中 pointerName 是指针变量名,typeName 是它所指向的值的类型。
    • 例如,var xPtr *int 声明了一个名为 xPtr 的指针,它指向一个整型(int)变量。
  3. 指针初始化与赋值

    • 初始化指针时,通常使用取地址运算符 & 来获取一个变量的内存地址,并将其赋值给指针变量。
    • 例如,x := 10; xPtr := &x,这里 xPtr 被初始化为变量 x 的内存地址。
  4. 解引用与指针操作

    • 使用指针访问或修改其指向的值时,需要通过解引用操作。解引用是使用指针变量前的星号 * 运算符来实现的。
    • 例如,*xPtr = 20 会通过 xPtr 修改其指向的整型变量的值为 20。
    • 同样,y := *xPtr 会通过 xPtr 获取其指向的值,并将其赋值给变量 y
  5. 指针运算限制

    • Go语言中的指针不能进行算术运算(如加减法),这是与C语言的重要区别。试图对指针进行算术运算会在编译阶段报错。
  6. 指针用途

    • 提高性能:通过指针传递大对象时,避免了值拷贝,提高了效率。
    • 修改函数外部变量:函数参数为指针时,函数内部可以修改外部变量的值。
    • 实现动态数据结构:如链表、树等,节点间通过指针连接。
    • 回调函数和闭包:闭包常常需要捕获并保持对外部变量的引用,这通常是通过指针实现的。

我们只需要记住

&是取地址,*是解引用,获取这个地址指向的值

四、init函数和defer函数

1.init函数

init函数用于在程序启动时执行一些初始化任务,如设置默认值、打开数据库连接、加载配置等。

init函数具有以下特点:

  1. 无须显式调用:init函数由Go运行时自动调用,无需在代码中显式调用。
  2. 无参数、无返回值:init函数没有参数,也不返回任何值。
  3. 可出现在任何包、任何源文件中:一个包(package)中可以有多个init函数,分布在不同的源文件中。

执行顺序:init函数的执行顺序遵循以下规则:

  1. 包内顺序:同一个包中,init函数按照它们在源文件中出现的顺序依次执行。如果一个源文件中有多个init函数,那么它们按照从上到下的顺序执行。
  2. 包间顺序:不同包之间的init函数按照包导入的依赖关系进行排序。首先执行被导入包(间接或直接)的所有init函数,然后才执行导入包本身的init函数。具体而言,如果包A导入了包B,那么包B中的所有init函数将在包A中的所有init函数之前执行。
  3. 主包(main包):主包(包含main()函数的包)的init函数总是在所有非主包的init函数之后执行。

2.defer函数

defer语句用于延迟执行一个函数调用,直到包含该defer语句的函数返回时才执行。defer语句通常用于资源清理、解锁、关闭文件、网络连接等需要确保在函数结束时执行的操作。

defer函数具有以下特点:

  1. 延迟执行defer语句指定的函数调用会在包含它的函数(或方法)即将返回时,无论正常返回还是通过panic异常返回,都会被执行。这种特性使得defer函数特别适合用于确保资源的正确释放,即使在函数执行过程中发生错误或异常。

  2. 后进先出(LIFO)顺序 :在同一函数中,如果有多个defer语句,它们的执行顺序与声明顺序相反,即最后声明的defer函数最先执行,最先声明的defer函数最后执行。这种顺序类似于栈的行为。

  3. 参数求值defer语句中的函数参数在defer语句执行时(即函数调用前)就已经求值,但函数体的执行是在函数返回时。

注意事项:

  1. defer函数的返回值会被计算,但会被丢弃,因此defer通常用于无返回值的函数调用。
  2. 大量使用defer可能会对函数的性能造成轻微影响,尤其是在函数调用非常频繁的场景。应合理使用,避免过度依赖。
  3. 如果defer函数内部引发了panic,后续的defer函数仍会按照后进先出的顺序执行,直到遇到recover函数捕获该panic,或者函数结束时panic传播到上层调用者。

3.defer面试题

Go 复制代码
package main

func DeferFunc1(i int) (t int) {
	// t = 1
	t = i
	defer func() {
		t += 3
	}()
	return t
}
func DeferFunc2(i int) int {
	t := i
	defer func() {
		t += 3
	}()
	return t
}
func DeferFunc3(i int) (t int) {
	defer func() {
		t += i
	}()
	return 2
}

// DeferFunc1中,有名返回(指定返回值命名func test() (t int)),执行 return 语句时,并不会再创建临时变量保存,defer 语句修改了t,即对返回值产生了影响,所以返回4。
// DeferFunc2中,无名返回(返回值没有指定命名),执行Return语句后,Go会创建一个临时变量保存返回值,defer 语句修改的是 t,而不是临时变量,所以返回1。
// DeferFunc3中,有名返回,执行 return 语句时,把2赋值给t,defer 语句再执行t+1,所以返回3。
func main() {
	println(DeferFunc1(1))
	println(DeferFunc2(1))
	println(DeferFunc3(1))
}
相关推荐
刘翔在线犯法几秒前
Scala例题
开发语言·后端·scala
anqi27几秒前
Scala 的List
开发语言·后端·scala
Word的妈呀几秒前
Scala的case class
开发语言·后端·scala
mit6.82421 分钟前
[Docker#5] 镜像仓库 | 命令 | 实验:搭建Nginx | 创建私有仓库
linux·后端·docker·云原生
武昌库里写JAVA38 分钟前
mysql 几种启动和关闭mysql方法介绍
java·开发语言·算法·spring·log4j
豆豆1 小时前
如何选择企业网站模版来搭建网站?
服务器·开发语言·前端·php·软件构建
吃葡萄不图葡萄皮1 小时前
3D绘制动态爱心Matlab
开发语言·matlab
星晨羽1 小时前
esayExcel根据模板导出包含图片
java·开发语言·windows
Say Bay To The Bugs1 小时前
EasyExcel 使用多线程按顺序导出数据
开发语言·excel
机器懒得学习1 小时前
Python & PyQt5 实现 .his 文件批量转 Excel 工具
开发语言·python·excel