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))
}
相关推荐
masa0104 分钟前
JavaScript--JavaScript基础
开发语言·javascript
拓端研究室TRL7 分钟前
Python用TOPSIS熵权法重构粮食系统及期刊指标权重多属性决策MCDM研究|附数据代码...
开发语言·python·重构
一只特立独行的猪6111 小时前
Java面试——集合篇
java·开发语言·面试
吃面不喝汤661 小时前
Flask + Swagger 完整指南:从安装到配置和注释
后端·python·flask
大得3692 小时前
go注册中心Eureka,注册到线上和线下,都可以访问
开发语言·eureka·golang
讓丄帝愛伱2 小时前
spring boot启动报错:so that it conforms to the canonical names requirements
java·spring boot·后端
weixin_586062022 小时前
Spring Boot 入门指南
java·spring boot·后端
小珑也要变强3 小时前
队列基础概念
c语言·开发语言·数据结构·物联网
AI原吾6 小时前
掌握Python-uinput:打造你的输入设备控制大师
开发语言·python·apython-uinput
机器视觉知识推荐、就业指导6 小时前
Qt/C++事件过滤器与控件响应重写的使用、场景的不同
开发语言·数据库·c++·qt