go语言基础函数,数组,指针,结构体

Posted 一只小阿大:)

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了go语言基础函数,数组,指针,结构体相关的知识,希望对你有一定的参考价值。

目录

函数

函数定义格式
func function_name( [parameter list] ) [return_types]
函数体

package main

import "fmt"

func test(num1 int,num2 int) int
	return num1+num2


func main() 
	fmt.Printf("total num = %d",test(1,2))


Opetion results:
total num = 3

go语言的函数返回值是可以多个函数返回值的

package main

import "fmt"

//  函数名 参数 参数类型        返回值类型
func test(num1 int,num2 int) (int,string)
	return num1+num2,"nihao"


func main() 
	a,b := test(1,2)
	fmt.Printf("total num = %d ,string = %s",a,b)


Operation results:
total num = 3 ,string = nihao

匿名函数

package main

import "fmt"

func main() 
	func() 
		fmt.Println("匿名函数")
	() // 这个小括号代表匿名函数直接运行

	//或者这样使用
	test := func() 
		fmt.Println("匿名函数")
	
	test()


Operation results:
匿名函数
匿名函数

函数闭包的简单使用以及获取键盘输入

闭包 = 函数 + 外层变量引用
为什么要使用函数闭包?
因为使用函数闭包可以减少全局变量使用,下一次进入闭包函数,闭包函数中的变量值不会更改,相当于全局变量了

简单使用:

package main

import "fmt"

func test() func() string
	return func() string
		var name string
		fmt.Println("Please input name")
		fmt.Scanf("%s",&name)
		fmt.Println(name)
		return name
	


func main()
	name := test()
	fmt.Println(name())


Operation results:
Please input name
Finny
Finny
Finny

闭包意义体现,以及执行步骤:
看完下面例子,会发现闭包函数中的变量不会释放

package main

import (
	"fmt"
)

//定义了一个闭包函数
func adder(a int) func(int) int 
	num := a //变量初始化
	fmt.Println(num)
	return func(x int) int  //闭包函数,需要注意的是闭包函数返回值
		num += x
		// fmt.Println(num)
		return num
	


func main() 
	myAdder := adder(10)
	
	// 从1加到5
	for i := 1; i <= 5; i++ 
		myAdder(i)
	
	
	fmt.Println(myAdder(0))
	// 再加上5
	fmt.Println(myAdder(5))


Operation results:
10
25
30

闭包加上函数多返回值

package main

import "fmt"

func calc(base int) (func(int) int, func(int) int) 
	add := func(a int) int 
		base += a
		return base
	

	sub := func(b int) int 
		base -= b
		return base
	

	return add, sub


func main() 
	x, y := calc(200)
	fmt.Println(x(200))
	fmt.Println(y(100))


Operation results:
400
300

为什么闭包不会被垃圾回收

为什么闭包不会被垃圾回收
因此闭包会长期占用内存资源,可能会导致内存泄漏

defer

package main

import "fmt"

func calc(index string, a, b int) int 
	ret := a + b
	fmt.Println(index, a, b, ret)
	return ret


/*
x = 1 ,y = 2
defer calc外面那层,里面那层不受外面defer影响 CALC("A", 1, 2)
输出 A 1 2 3

X = 10
calc("B", 10, 2)
输出 B 10 2 12

y = 20
因为最先defer的最后执行
所以执行calc("BB", x, calc("B", x, y)) 也就是 calc("BB", 10, 12)
输出 BB 10 12 22

最后执行calc("AA", x, calc("A", x, y)) 也就是 calc("AA", 1, 3)
输出 AA 1 3 4
值还是那时候的值,注意是延迟执行!
*/
func main() 
	x := 1
	y := 2
	defer calc("AA", x, calc("A", x, y))
	x = 10
	defer calc("BB", x, calc("B", x, y))
	y = 20


Operation results:
A 1 2 3
B 10 2 12
BB 10 12 22
AA 1 3 4

内置函数panic、recover


panic 和 recover 常搭配 defer使用,recover只在defer中有效,defer,recover组合要在panic之前使用,要不直接异常退出了,这三个配合有点像java里面的try catch

Recover 是一个Go语言的内建函数,可以让进入宕机流程中的 goroutine 恢复过来,recover 仅在延迟函数 defer 中有效,在正常的执行过程中,调用 recover 会返回 nil 并且没有其他任何效果,如果当前的 goroutine 陷入恐慌,调用 recover 可以捕获到 panic 的输入值,并且恢复正常的执行。

通常来说,不应该对进入 panic 宕机的程序做任何处理,但有时,需要我们可以从宕机中恢复,至少我们可以在程序崩溃前,做一些操作,举个例子,当 web 服务器遇到不可预料的严重问题时,在崩溃前应该将所有的连接关闭,如果不做任何处理,会使得客户端一直处于等待状态,如果 web 服务器还在开发阶段,服务器甚至可以将异常信息反馈到客户端,帮助调试。
提示
在其他语言里,宕机往往以异常的形式存在,底层抛出异常,上层逻辑通过 try/catch 机制捕获异常,没有被捕获的严重异常会导致宕机,捕获的异常可以被忽略,让代码继续运行。

Go语言没有异常系统,其使用 panic 触发宕机类似于其他语言的抛出异常,recover 的宕机恢复机制就对应其他语言中的 try/catch 机制。

package main

import "fmt"

func testFuncA() 
	fmt.Println("testFuncA OK!")


func testFuncB() 
	defer func() 
		err := recover()
		if err != nil 
			fmt.Println("Func B error")
		
	()

	panic("testFuncB Exception")


func testFuncC() 
	fmt.Println("testFuncA OK!")


func main() 
	testFuncA()
	testFuncB()
	testFuncC()


Operation results:
testFuncA OK!
Func B error
testFuncA OK!

语言变量作用域

Go 语言中变量可以在三个地方声明:

函数内定义的变量称为局部变量
函数外定义的变量称为全局变量
函数定义中的变量称为形式参数

需要注意的一点是,go语言可以定义同名的全局变量和局部变量,但是局部变量的优先级大于全局变量的优先级

package main

import (
	"fmt"
)

var a int = 10

func main() 
	var a int = 20
	fmt.Println(a) 


Operation results:
20

数组

数组定义

package main

import (
	"fmt"
)

func main() 
	var a [3]int            //数组定义了未初始化
	var b = [3]int1,2,3   //数组定义了按顺序初始化
	var c = [3]int1:3,2:1 //数组定义了按索引初始化
	var d = [3]int1,2:19  //数组定义了混合初始化(顺序和索引)
	//可以使用编译器推导长度 var b = [...]int1,2,3
	var i int
	fmt.Println("Array output of A")
	for i=0 ; i<3 ; i++
		fmt.Println(a[i])
	

	fmt.Println("Array output of B")
	for i=0 ; i<3 ; i++
		fmt.Println(b[i])
	

	fmt.Println("Array output of C")
	for i=0 ; i<3 ; i++
		fmt.Println(c[i])
	

	fmt.Println("Array output of D")
	for i=0 ; i<3 ; i++
		fmt.Println(d[i])
	


Operation results:
Array output of A
0
0
0
Array output of B
1
2
3
Array output of C
0
3
1
Array output of D
1
0
19

数组+函数+宏定义

似乎go语言没有宏定义这个概念,但是我觉得const关键字相当于C语言宏定义define 只是从使用上来看哦,底层原理应该是不一样的。

package main

import (
	"fmt"
)
//Macro definition
const (Arraysize = 5)

//Func
func test(Array [Arraysize]int, Size int)int
	i := 0
	var ArrayTotal int = 0
	for i=0;i<Size;i++
		fmt.Println(Array[i])
		ArrayTotal += Array[i]
	
	return ArrayTotal


//Main
func main() 
	var Array = [Arraysize]int11,2,45,6,3              
	fmt.Println("Total array =",test(Array, Arraysize),"ok!")
	


Operation results:
11
2
45
6
3
Total array = 67 ok!

二维数组

package main

import "fmt"

func main() 
	var Array = [3][2]string
		"北京", "上海",
		"苏州", "无锡",
		"广州", "深圳",
	
	fmt.Println(Array)

	//二维数组遍历
	for _,v1 := range Array
		for _,v2 := range v1
			fmt.Println(v2)
		
	


Operation results:
[[北京 上海] [苏州 无锡] [广州 深圳]]
北京
上海
苏州
无锡
广州
深圳

指针

跟C语言一样,只需要记住指针也是个变量,跟int整形一样,只不过他是存放地址的变量
*在定义的时候只代表这是个几级指针,不代表取值
&是取地址
记清楚这两个符号,玩指针基本没问题

new(int)函数和直接定义的区别就是,new函数会初始化,直接定义的不会初始化

package main

import "fmt"

func main() 
	var a int = 10
	var p *int = &a
	p2 := new(int)
	p2 = &a

	fmt.Printf("Variable address: %x\\n", &a)
	fmt.Printf("Variable address: %x\\n", p)
	fmt.Printf("Variable address: %x\\n", p2)


Operation results:
Variable address: c0000aa058
Variable address: c0000aa058
Variable address: c0000aa058

make和new的区别

make也是用于内存分配的,区别于new,它只用于slice、map以及chan的内存创建,而且它返回的类型就是这三个类型本身,而不是他们的指针类型,因为这三种类型就是引用类型,所以就没有必要返回他们的指针了。func make(t Type, size …IntegerType) Type

func make(t Type, size …IntegerType) Type

  1. 二者都是用来做内存分配的。
  2. make只用于slice、map以及channel的初始化,返回的还是这三个引用类型本身;
  3. 而new用于类型的内存分配,并且内存对应的值为类型零值,返回的是指向类型的指针。

二级指针的使用

package main

import "fmt"

func main() 
   var a int = 10     //整形
   var p1 *int = &a   //一级指针
   var p2 **int = &p1 //二级指针

   fmt.Printf("Variable address: %x\\n", &a  )
   fmt.Printf("Variable address: %x\\n", p1  )
   fmt.Printf("Variable address: %x\\n", &p1  )
   fmt.Printf("Variable address: %x\\n", p2  )
   fmt.Printf("Variable address: %x\\n", *p2  )
   
   fmt.Printf("Variable content: %d\\n", a  )
   fmt.Printf("Variable content: %d\\n", *p1  )
   fmt.Printf("Variable content: %d\\n", **p2  )

Operation results:
Variable address: c000012088
Variable address: c000012088
Variable address: c000006028
Variable address: c000006028
Variable address: c000012088
Variable content: 10
Variable content: 10
Variable content: 10

空指针

当一个指针被定义后没有分配到任何变量时,它的值为 nil。
nil 指针也称为空指针。
nil在概念上和其它语言的null、None、nil、NULL一样,都指代零值或空值。
一个指针变量通常缩写为 ptr

package main

import "fmt"

func main() 
   var p1 *int

   fmt.Println(p1)


Operation results:
< nil >

指针数组

package main

import "fmt"

const MAX int = 3

func main() 
   a := []int10,100,200
   var i int
   var ptr [MAX]*int;

   for  i = 0; i < MAX; i++ 
      ptr[i] = &a[i] /* 整数地址赋值给指针数组 */
   

   for  i = 0; i < MAX; i++ 
      fmt.Printf("a[%d] = %d\\n", i,*ptr[i] )
   

Operation results:
a[0] = 10
a[1] = 100
a[2] = 200

将指针传入函数进行值的交换

package main

import "fmt"

func exchange(p1 *int, p2 *int)int
	*p1 , *p2 = *p2 , *p1

	return 1


func main() 
	var a int = 10
	var b int =2022
	var p1 *int = &a
	var p2 *int = &b

	fmt.Println(*p1)
	fmt.Println(*p2)
	if exchange(p1, p2) == 1
		fmt.Println("exchange ok!")
	
	fmt.Println(*p1)
	fmt.Println(*p2)


Operation results:
10
2022
exchange ok!
2022
10

结构体

自定义类型和类型别名

package main

import "fmt"

//自定义类型
type UINT16 uint16

//类型别名
type UINT8 = uint8

func main() 
	var a UINT16
	var b UINT8
	fmt.Printf("Type = %T Value = %v\\n", a, a)
	fmt.Printf("Type = %T Value = %v\\n", b, b)


Operation results:
Type = main.UINT16 Value = 0
Type = uint8 Value = 0

结构体初始化

package main

import "fmt"

type Human struct 
	name, city string
	age        uint8


func main() 
	//结构体键值对初始化
	man1 := Human
		name: "zhangsan",
		age:  18,
	

	fmt.Printf("%#v\\n", man1)

	man2 := Human
		name: "zhangsan",
		city: "suzhou",
		age:  32,
	
	fmt.Printf("%#v\\n", man2)

	//值初始化,要按照顺序
	woman1 := Human
		"wangwu",
		"beijing",
		21,
	
	fmt.Printf("%#v\\n", woman1)


Operation results:
main.Humanname:“zhangsan”, city:"", age:0x12
main.Humanname:“zhangsan”, city:“suzhou”, age:0x20
main.Humanname:“wangwu”, city:“beijing”, age:0x15

匿名结构体

package main

import "fmt"

func main() 
	//匿名结构体
	var HideStruct struct 
		name string
		age  uint8
	

	HideStruct.name = "Finny"
	HideStruct.age = 21

	fmt.Printf("%#v\\n", HideStruct)
	fmt.Println(HideStruct.name)
	fmt.Println(HideStruct.age)


Operation results:
struct name string; age uint8 name:“Finny”, age:0x15
Finny
21

结构体的内存布局

package main

import "fmt"

type Human struct 
	a int8
	b int8
	c int8
	d int8


func main() 
	n := Human
		1, 2, 3, 4,
	
	b := Human
		1, 2, 3, 4,
	

	fmt.Printf("n.a %p\\n", &n.a)
	fmt.Printf("n.b %p\\n", &n.b)
	fmt.Printf("n.c %p\\n", &n.c)
	fmt.Printf("n.d %p\\n", &n.d)

	fmt.Printf("b.a %p\\n", &b.a)
	fmt.Printf("b.b %p\\n", &b.b)
	fmt.Printf("b.c %p\\n", &b.c)
	fmt.Printf("b.d %p\\n", &b.d)


Operation results:
n.a 0xc000012088
n.b 0xc000012089
n.c 0xc00001208a
n.d 0xc00001208b
b.a 0xc00001208c
b.b 0xc00001208d
b.c 0xc00001208e
b.d 0xc00001208f

结构体内存对齐

各变量类型所占

以上是关于go语言基础函数,数组,指针,结构体的主要内容,如果未能解决你的问题,请参考以下文章

Go语言面向函数和接口编程

Go语言基础:结构体

Go语言基础:结构体

Go基础4

Go基础4

Go基础4