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
- 二者都是用来做内存分配的。
- make只用于slice、map以及channel的初始化,返回的还是这三个引用类型本身;
- 而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语言基础函数,数组,指针,结构体的主要内容,如果未能解决你的问题,请参考以下文章