go中函数,包,机制

说明: 该文章是笔者的一个记录,不喜可以直接关闭退出,勿喷

包的概念

go中每一个文件都是属于一个包的,也就是说go以包的形式管理文件和目录结构的

包的作用

  1. 区分相同名称的函数,变量等标识符
  2. 当程序文件很多时,可以很好的管理项目
  3. 控制函数,变量等访问范围,即作用域

包的注意事项

  1. 每一个包下有一个文件,文件的包名通常和文件所在的包的名称相同,一般为小写字母
  2. 当一个文件要使用另一个文件中的函数或变量时,需要在import中导入对应的包
  3. 为了让其他包的文件可以访问到本包的函数,那么需要函数名的首字母大写
  4. 在访问其他包的函数,变量时,其语法为 包.函数名
  5. 如果包名较长,Go支持给包取别名,注意: 如果给包名取了别名后,那么原来的包名不可用
  6. 在同一个包下,不能有相同的函数名,也不能有相同的全局变量名

闭包

概念

闭包就是一个函数和与其相关的引用环境组合的一个整体
也可以理解为:在一个函数中,他的返回值为匿名函数,并且匿名函数和匿名函数外的变量构成的一个整体,称为闭包
示例:

func test() func(int) int {
    
    
	var n int = 10
	return func(x int) int {
    
    
		n += x
		return n
	}
}
func main(){
    
    
	res := test()
	fmt.Println(res)
}

函数的defer

概念

在函数中,程序员经常需要创建资源,为了在函数执行完毕后,及时的释放资源,go提供了defer延时机制

defer注意细节

  1. 当go执行到一个defer时,不会立即执行defer后的语句,而是将defer后的语句压入到一个栈中,然后继续执行函数的下一个语句
  2. 当函数执行完毕后,再从defer栈中,依次从栈顶取出语句执行
    示例:
func test(n1, n2 int){
    
    
	defer fmt.Println("ok1=}", n1) // 后执行
	defer fmt.Println("ok2=}", n2) // 先执行
	n1++;
	n2++;
}
func main(){
    
    
	test(1, 2)
}

函数调用过程

  1. 当调用一个函数时,会给函数分配一个新的空间,编译器会通过自身的处理让这个新的空间和其他的栈的空间区分开来
  2. 在每个函数对应的栈中,数据空间是独立的,不会混淆
  3. 当一个函数调用完毕后,程序会销毁这个函数对应的栈空间

函数参数传递方式

  1. 值传递
  2. 引用传递

参数传递时,无论值传递还是i引用传递,都是传递的是变量的副本,不同的是,值传递的是值的拷贝,而引用传递是地址的拷贝。值传递的话,函数中的值改变不会影响原函数中数值的变化,而引用传递的话,函数中的值改变会影响原函数中数值的变化。

值类型和引用类型

值类型: string int float 数组 bool struct
引用类型: 指针,slice切片,map集合,chan 管道,interface

匿名函数的使用

创建匿名函数方式

  1. 在定义匿名函数时就直接调用,这种方法只能调用一次
func main(){
    
    
	res := func test(n1, n2 int) int {
    
    
		return n1 + n2
	}(10, 10)
	fmt.Println(res)
}
  1. 将匿名函数赋值给一个变量,再通过这个变量来调用匿名函数
func main(){
    
    
	res := func test(n1, n2 int) int {
    
    
		return n1 + n2
	}
	res1 := res(10, 10)
	fmt.Println(res1)
}
  1. 全局匿名函数
var (
	Fun = func (n1 int, n2 int) int {
    
    
		return n1 + n2
	}
)
func main(){
    
    
	res := Fun(10, 10)
	fmt.Println(res)
}

字符串常用的系统函数

  1. 统计字符串的长度
str := "hello go"
len(str)
  1. 字符串遍历 含汉字
str := "hello 中国"
str1 := []rune(str)
for i := 0; i < len(str1); i++ {
	fmt.Println(str1[i])
}
  1. 字符串转整数
n, err := strcov.Atoi("12")
  1. 整数转字符串
n, er := strcov.Itoa("12")
  1. 字符串转[]byte数组
var bytes = []byte{"hello"}
  1. []byte数组转字符串
str := string([]byte{1, 2})
  1. 十进制转2, 8, 16进制
// 2 -- 表示几进制    123 -- 表示10进制的值
str := strcov.FormatInt(123, 2)
  1. 查找字串是否存在指定的字符串
b := strings.Contains("heelo", "ee")
// b 为bool值
  1. 统计一个字符串有几个指定的字符串
num := strings.Count("ceeeee", "e")
  1. 字符串比较(不分大小写)
b = strings.EqualFold("abc", "Abc")
// b 为bool型

错误机制

  1. 使用defer+recover()来处理错误
func test(){
    
    
	defer func(){
    
    
		err := recover()
		if err != nil {
    
    
			fmt.Println("err=", err)
		}
	}
	num1 := 10
	num2 := 0
	res := num1 / num2
	fmt.Println(res)
}

使用defer+recover()机制,进行错误处理后,程序不会轻易挂掉。
2. 自定义错误
使用errors.New 和 panic内置函数
errors.New(“错误信息”) 使用errors.New和panic内置i函数
panic内置函数,接收一个interface{}类型的值作为参数。可以接收error类型的变量,输出错误信息,并退出程序

func readConf(name string)(err error) {
    
    
	if name == "config.ini" {
    
    
		return nil
	}else {
    
    
		return errors.New(""错误信息)
	}
}
func test(){
    
    
	err := readConf("config.int")
	if err != nil {
    
    
		panic(err)
	}
	fmt.Println("....")
}

数组和切片

四种初始化数组的方式

	var number1 = [3]int{1, 2, 3}
	fmt.Println("number1=", number1)
	var number2 = [...]int{1, 2, 3}
	fmt.Println("number2=", number2)
var number3 = [...]int{1: 800, 0: 13, 2: 567}
fmt.Println("number3=", number3)

切片创建方式

方式一 是直接引用数组 这个数组是事先存在的 程序员不可见的

var intArr = [...]int{1, 22, 33, 65, 99}
// 第一种方式 定义一个切片 让切片去引用一个已经创建好的数组
slice := intArr[0:5]
fmt.Println("intArr=", intArr)

方式二 通过make来创建切片 make也会创建一个数组 是由切片在底层进行维护 程序员看不见的

	// make创建  数据类型 大小 指定切片容量
	var slice1 []float64 = make([]float64, 5, 10)
	fmt.Println(slice1)
	
	// 用append 内置函数 可以对切片进行动态追加
	var slice3 = []int{100, 200, 300}
	slice3 = append(slice3, 400, 500, 600)
	fmt.Println("slice3", slice3)

	// 通过append将切片slice3追加到slices3
	slice3 = append(slice3, slice3...)
	fmt.Println(slice3)

切片append操作的底层原理分析:

  1. 切片append操作本质就是对数组扩容
  2. go底层会创建一个新的数组 newArr(安装扩容后的大小)
  3. 将slice原来包含的元素拷贝到新的数据newArr
  4. 将slice重新引用到newArr

修改string值

因为string是不可修改的 ,若要修改,有两种方式,方式一,将string->[]byte,方式二,将string->[]rune->修改->string

str := "hello@atguigu"
	slice6 := str[6:]
	fmt.Println("slice6=", slice6)
	//  string 是不可变的  若想改变 可以将string->[]byte 
	//或者 []rune->修改->重写转成string
	arr23 := []byte(str)
	arr23[0] = 'z'
	str = string(arr23)
	fmt.Println("str=",str)

	// 我们转成[]byte后,可以处理英文和数字 但是不能处理中文
	// 原因是[]byte 字节来处理 而一个汉字 3个字节 因此就会出现乱码
	// 解决方式 将string 转成 []rune 即可 因为[]rune是按字符处理 兼容汉字
	arr24 := []rune(str)
	arr24[0] = '北'
	str = string(arr24)
	fmt.Println("str=", str)

公众号

希望大家多多关注,里面不定期发放干货
领取全套资料:回复关键字【666】
迈莫公众号

猜你喜欢

转载自blog.csdn.net/qq_41066066/article/details/105714101
今日推荐