01.函数定义
什么是函数?
从前,人们想要制作果汁,每次都用手捏,非常麻烦,后来有人发明了榨汁机,人们只要把水果放进去,榨汁机就会把果汁榨出来。函数的功能就像榨汁机一样,帮助人们重复的做任务。函数是组织好的、可重复使用的执行特定任务的代码块。它可以提高应用程序的模块性和代码的重复利用率。Go语言支持普通函数、匿名函数和闭包,从设计上对函数进行了优化和改进,让函数使用起来更加方便。函数本身可以作为值进行传递。
package main //指定其所在的包
import "fmt" //导入fmt包
func main() { //main()函数是程序的入口
fmt.Println("不忘初心,牢记使命 ! ") //Println()函数打印内容
}
- Go语言程序中都有一个main()函数
- 可以通过其他函数来实现不同功能,逻辑上每个函数执行的是指定的任务
- Go语言标准库提供了多种可用的内置函数
- 函数的基本组成
- 关键字func
- 函数名
- 参数列表
- 返回值
- 函数体
- 返回语句
- 函数的特点
- Go语言的编译型语言,函数编写时顺序的无关紧要的
- 函数构成了代码执行的逻辑结构
- 函数是基本的代码块
- 将一个需要很多行代码的复杂问题分解为一系列简单的任务来解决
- 同一个任务(函数)可以被多次调用,有助于代码重用
package main
import "fmt"
func main() {
fmt.Println(sum(1,2,3,4,5))
}
func sum(numbers ...int) int{
s := 0
for i := range numbers{
s += numbers[i]
}
return s
}
//运行结果为:
15
- 当函数执行到代码块最后一行 } 之前或者return语句的时候会退出
- 其中return语句可以带有零个或者多个参数,这些参数将作为返回值供调用者使用
- Go语言函数的支持特性
支持:
- 支持参数数量不固定
- 支持匿名函数及闭包
- 支持函数本身作为值传递
- 支持函数的延迟执行
- 支持函数作为接口调用
- 无须前置声明
- 支持多返回值
- 支持命名返回值
不支持:
- 不支持命名嵌套定义
- 不支持同名函数重载
- 不支持默认参数
- Go语言里面拥有3种类型的函数
2.
3.
方法是*绑定到某种类型*的(通常是结构体)
普通函数:不与任何类型绑定,可以在任何地方调用,不需要依赖某个特定的类型。
方法: 与某个类型绑定,需要通过该类型的实例来调用,适合封装类型的行为和操作。
package main
import "fmt"
// 定义一个结构体
type Person struct {
name string
age int
}
// 定义一个方法,接收者是Person类型
func (p Person) greet() {
fmt.Println("Hello, my name is", p.name)
}
// 定义一个带指针接收者的方法,用于修改属性
func (p *Person) haveBirthday() {
p.age += 1
fmt.Println(p.name, "is now", p.age, "years old")
}
func main() {
// 创建一个Person实例
person := Person{name: "Alice", age: 30}
person.greet()
person.haveBirthday() }
02.函数创建
- 函数的语法:
package main
import (
"fmt"
"math"
)
func main() {
fmt.Println(hypot(3,4))
}
func hypot(x, y float64) float64 {
return math.Sqrt(x*x + y*y)
}
//运行结果为:
5
在hypot函数中:x和y是形参名,3和4是调用时传入的实数,函数返回了一个float64类型的值,返回值也可以像形式参数一样被命名,在这种情况下,每个返回值被声明成一个局部变量,并根据该返回值的类型,将其初始化为0.
- 函数的语法详解:
- func:函数是以func为关键字;
- 函数名:函数名由字母、数字和下划线构成,但是函数名不能以数字开头;在同一个包内函数名不可重复;
- 参数列表:参数列表中的每个参数都由参数名称和参数类型两部分组成,参数变量为函数的局部变量。如果函数的参数数量不固定,Go语言还支持可变参数;
- 返回参数列表:返回参数列表中的每个参数由返回的参数名称和参数类型组成,也可简写为返回值类型列表;
- 函数体:函数体指函数的主体代码逻辑,若函数有返回参数列表,则函数体中必须有return语句返回值列表。
- 参数:
- 函数可以有一个或者多个参数。
- 如果函数使用参数,则该参数被称为函数的形参。
- 形参就像定义在函数体内的局部变量。
形参:在定义函数时,用于接受外部传入的数据被称为形式参数
实参:在调用函数时,传给形参的实际的数据被称为实际参数
Go函数还支持可变参数(简称“变参”)。接受变参的函数有着不定数量的参数。
- Go语言函数中的传递方式:
值传递:
1.在调用函数时将实际参数复制一份传递到函数中
2.函数运行中如果对参数进行修改,将不会影响到实际参数
引用传递:
1.在调用函数时将实际参数的地址传递到函数中
2.函数运行中对参数所进行的修改,将影响到实际参数
package main
import "fmt"
func number1(num int){
num = 2
}
func number2(num *int){
*num = 2
}
func main(){
a := 1
number1(a)
fmt.Println(a)//值传递
number2(&a)
fmt.Println(a)//引用传递
}
//运行结果为:
1
2
默认情况下,Go语言所使用的是值传递,即在调用过程中不会影响到实际参数
函数参数调用需遵守如下形式:
(a) 函数名称必须匹配;
(b)实参与形参必须一一对应:顺序、个数、类型。
03.函数变量
- 函数变量的语法:
var 变量名称 func()
- 函数变量:
Go语言中,函数也是一种类型,我们可以将其保存在变量中
- 可以通过type定义这种变量的类型
- 拥有相同参数和相同返回值的函数属于同一种类型
- 其优点是:可以将函数作为值进行传递
package main
import "fmt"
func main() {
a :=10
b :=11
var f1 func(x int,y int) (sum int,sub int)
f1 = addSub
sum ,sub := f1(a,b)
fmt.Printf("%d+%d=%d\n", a,b,sum)
fmt.Printf("%d-%d=%d\n", a,b,sub)
}
func addSub(x int,y int) (sum int,sub int) {
sum = x+y
sub = x-y
return sum,sub
}
//运行结果为:
10+11=21
10-11=-1
var f1 func(x int,y int (sum int,sub int))中的 f1 为函数变量,声明后的 f1 可以被当成 函数类型的变量来使用。
04.指针创建
-
1.指针(pointer),全称为指针变量,是用来存储内存地址的一种变量。
-
2.程序中,一般通过指针来访问其指向的内存地址中的内容(数据)。
以上,实实在在的变量,有自己的内存存储空间,它可以指向任何有效的变量。
-
引用,是C++中提出的一种新的使用变量的方式,即给实际变量起个别名,通过这个别名来引用实际的变量。
以上,没有自己的实际存储空间。
-
指针声明:
var var_name *var_type
- 语法含义:
- var_name为指针变量名
- var_type为指针类型
- *用于指定变量是作为一个指针
- 这里的类型可以是之前的变量与常量的类型,例如int,float等。
package main
import "fmt"
func main() {
var a int= 1
var b *int
b = &a
fmt.Printf("a变量地址: %x\n", &a )
fmt.Printf("b变量指针地址: %x\n", b )
fmt.Printf("*b变量的值: %d\n", *b )
}
//运行结果为:
a变量地址: c00000a098
b变量指针地址: c00000a098
*b变量的值: 1
Go语言中的指针是一种地址值;
指针变量就是存放地址值的变量。
package main
import (
"fmt"
)
func main() {
var p *int
fmt.Println(p)
}
//运行结果为:
<nil>
var p *int为指针类型的声明,由于变量 p 没有复制,所以打印为空。
- 指针特点:
- 一个指针变量可以指向任何一个值的内存地址;
- 它所指向的值的内存地址在32和64位机器上分别占用4或8个字节;
- 占用字节的大小与所指向的值的大小无关;
- 当一个指针被定义后没有分配到任何变量时,它的默认值为nil;
- 指针变量通常缩写为ptr(指代point)。
- 每个变量在运行时都有用一个地址,这个地址代表变量在内存中的位置,Go语言中使用**“&”**符号放在变量前面对变量进行“取地址”操作。
ptr := &v // v的类型为T
//v代表被取地址的变量,
//变量ptr用于接收地址值,
//ptr的类型就为*T,
//称做T的指针类型,*代表指针。
package main
import (
"fmt"
)
func main() {
num :=1
var p *int
p = &num
fmt.Println("num变量的地址为:",p)
fmt.Println("指针变量p的地址为:",&p)
}
//运行结果为:
num变量的地址为: 0xc042066080
指针变量p的地址为: 0xc042086018
指针变量本身也有自己的地址。
- 在对普通变量使用 & 操作符取地址获得这个变量的指针后,可以对指针使用 “*” 操作符,也就是指针取值。
var house = "Malibu Point 10880, 90265"
ptr := &house
fmt.Printf("ptr type: %T\n", ptr)
fmt.Printf("address: %p\n", ptr)
value := *ptr
fmt.Printf("value type: %T\n", value)
fmt.Printf("value: %s\n", value)
//运行结果
ptr type: *string
address: 0xc0420401b0
value type: string
value: Malibu Point 10880, 90265
取地址操作符 & 和取值操作符 * 是一对互补操作符,& 取出地址,* 根据地址取出地址指向的值。
- 变量、指针地址、指针变量、取地址、取值和相互关系和特性
对变量进行取地址操作使用 &操作符,可以获得这个变量的地址值。
指针变量的值是地址值。
对指针变量进行取值操作使用*操作符,可以获得指针变量指向的原变量的值。
package main
import (
"fmt"
)
func main() {
num :=1
var p *int
p = &num
fmt.Println("指针变量p的所指向的内容为:",*p)
}
//运行结果为:
指针变量p的所指向的内容为:1
在取出指针的地址值后,通过在前面加*可以打印指针所指向的内容。
package main
import "fmt"
func main() {
// 定义局部变量
var a int = 100
var b int = 200
swap(&a, &b);
fmt.Printf("交换后 a 的值 : %d\n", a )
fmt.Printf("交换后 b 的值 : %d\n", b )
}
//将指针作为函数参数
func swap(x *int, y *int){
*x, *y = *y, *x
}
//运行结果为:
交换后 a 的值 : 200
交换后 b 的值 : 100
Go语言支持向函数传递指针,只需要将函数定义的参数,设置为指针类型即可。