【go语言】函数与指针

1.go函数中的三种返回方式

// 函数的返回值有一个,类型是int
func fun1(a string, b int) int {
	fmt.Println("--------fun1----------")
	fmt.Println("a=", a)
	fmt.Println("b=b", b)
	c := 100
	return c
}
func fun2(a string, b int) (string, int) {
	fmt.Println("--------fun2-----------")
	fmt.Println("a=", a)
	fmt.Println("b=", b)
	return "hello go", 2
}
func fun3(a string, b int) (r1 int, r2 int) {
	fmt.Println("--------fun3-----------")
	fmt.Println("a=", a)
	fmt.Println("b=", b)
	r1 = 100
	r2 = 200
	return
}
func main() {
	fun1_ret1 := fun1("a", 1)
	fun2_ret1, fun2_ret2 := fun2("a", 1)
	fun3_ret1, fun3_ret2 := fun3("a", 1)
	fmt.Printf("fun1_ret1=%d\n", fun1_ret1)
	fmt.Printf("fun2_ret1=%s,fun2_ret2=%d\n", fun2_ret1, fun2_ret2)
	fmt.Printf("fun3_ret1=%d,fun3_ret2=%d\n", fun3_ret1, fun3_ret2)
}

在这里插入图片描述

2.import导包路径和init方法

  • golang里面有两个保留的函数:init函数(能够应用于所有的package)和main函数(只能应用于package main)。这两个函数在定义时不能有任何的参数和返回值。

  • go程序会自动调用init()和main(),但是从程序的可读性和维护性,都建议只有一个init函数。每个package中的init函数都是可选的,但package main就必须包含一个main函数。

  • 程序的初始化和执行都起始于main包。如果main包还导入了其它的包,那么就会在编译时将它们依次导入。有时一个包会被多个包同时导入,那么它只会被导入一次。

  • 等所有被导入的包都加载完毕了,就会开始对main包中的包级常量和变量进行初始化,然后执行main包中的init函数(如果存在的话),最后执行main函数。下图详细地解释了整个执行过程:

在这里插入图片描述

注意:在包外调用的函数首字母需要大写

文件目录结构

在这里插入图片描述

lib1.go

package lib1
import "fmt"
func init(){
	fmt.Println("init lib1")
}
func Lib1(){
	fmt.Println("run Lib1")
}

lib2.go

package lib2
import "fmt"

func init() {
	fmt.Println("init lib2")
}
func Lib2() {	//包外调用的函数首字母需要大写
	fmt.Println("run Lib2")
}

main.go

import (
	"fmt"
	"in/lib1"
	"in/lib2"
)
func init() {
	fmt.Println("init main")
}
func main() {
	fmt.Println("run main")
	lib1.Lib1()
	lib2.Lib2()
}

 go run main.go

在这里插入图片描述

2.1指针声明

与C相同,Go语言让程序员决定何时使用指针。变量其实是一种使用方便的占位符,用于引用计算机内存地址。Go 语言中的的取地址符是&,放到一个变量前使用就会返回相应变量的内存地址。

指针变量其实就是用于存放某一个对象的内存地址。

go语言存在三种类型的指针:普通指针(*T),uintptr,unsafe.Pointer

*普通指针(T)

普通指针的用法和C语言的用法相同

func Ptr_test() {
	var a int = 4
	var b float32 = 11.32
	var c complex64 = complex(1, 2) //复试的定义方式
	//第一种定义方式
	var ptra *int = &a
	var ptrb *float32 = &b
	var ptrc *complex64 = &c
	fmt.Printf("type:%T,&p:%p,p:%d\n", ptra, ptra, *ptra)
	fmt.Printf("type:%T,&p:%p,p:%f\n", ptrb, ptrb, *ptrb)
	fmt.Printf("type:%T,&p:%p,p:%v\n", ptrc, ptrc, *ptrc)
}

在这里插入图片描述

uintptr

uinptr是一个无符号的整形,可以保存一个指针地址。可以进行指针运算

uintptr无法持有对象, GC不把uintptr当指针, 所以uintptr类型的目标会被回收。想取值需要转成unsafe.Pointer后, 需再转到相对应的指针类型。

unsafe.Pointer

unsafe.Pointer可以指向任意类型的指针。不能进行指针运算,不能读取内存存储的值(想读取的话需要转成相对应类型的指针)。它是桥梁,让任意类型的指针实现相互转换, 也可以转换成uintptr 进行指针运算。

一般的指针运算会走一下三个步骤。
1.将unsafe.Pointer转换为uintptr => 2.对uintptr执行算术运算 => 3.将uintptr转换回unsafe.Pointer,然后转成访问指向的对象的指针类型。

示例

func Uintptr_test() {
	a := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
	b := unsafe.Pointer(uintptr(unsafe.Pointer(&a[0])) + 9*unsafe.Sizeof(a[0]))

	// b是 unsafe.Pointer 所以可转任意指针,转成(*int)指针后在取值
	fmt.Printf("b: %v, unsafe.Sizeof(a[0]): %d\n", *(*int)(b), unsafe.Sizeof(a[0])) //b: 9, unsafe.Sizeof(a[0]): 8

	c := unsafe.Pointer(uintptr(unsafe.Pointer(&a)) + uintptr(16)) //int是8位长度 所以16 等于 16/8 挪动了2位,所以下面结果是2
	fmt.Printf("c: %v\n", *(*int)(c))                              //c: 2
}

在这里插入图片描述

实现swap()函数

func swap(a *int, b *int) {
	var tmp int = *a
	*a = *b
	*b = tmp
}
func main() {
	var a int = 4
	var b int = 5
	fmt.Println("before swap,a:%d,b:%d", a, b)
	swap(&a, &b)
	fmt.Println("after swap,a:%d,b:%d", a, b)
}

在这里插入图片描述

2.2空指针

当一个指针被定义后没有分配任何变量时,它的值为nil,也就是空指针,与C语言中的NULL一致。

3.defer

defer语句被用于预定对一个函数的调用。可以把这类被defer语句调用的函数称为延迟函数。

defer作用:

  • 释放占用的资源
  • 捕捉处理异常
  • 输出日志

defer一般用于资源回收或者异常捕获,defer后面的语句会在return执行之再执行。

如果一个函数中有多个defer语句,它们会以LIFO(后进先出)的顺序执行。【defer的底层其实是一个栈,底层存储结构是个链表,每次程序进入函数且运行到defer时,会把其后面的函数放到链表里】

在这里插入图片描述

func demo() {
	defer fmt.Println("1")
	defer fmt.Println("2")
	defer fmt.Println("3")
	defer fmt.Println("4")
}
func main() {
	demo()
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

影中人lx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值