# Go语言语法基础入门(下) | 青训营
2023/8/15 ·雨辰login
该篇笔记在7/30日发过一次,但是当天发了中和下两篇,不符合笔记活动要求(一天一篇),故在今天重新发一遍。
目录:
声明
变量
复合数据类型
---(中篇开始)---
循环
分支
补充:标准输出
---(下篇开始)---
函数
面向对象
上篇指路:Go语言语法基础入门 | (上)
中篇指路:Go语言语法基础入门 | (中)
鸣谢:
玉米哥:Go语言入门指南(下) | 青训营的文章给我的灵感和启示,十分感谢
Fengfeng-docs 枫枫知道官方文档:部分内容引用自该篇文档
来到Go基础语法入门的最后一篇了,本篇文章我们着重介绍函数和结构体的内容,会通过对比cpp和python中的相关内容来让读者有更深层的理解。话不多说,我们现在开始。
函数
标准函数的写法:
func Sum(n1 int, n2 int) int{
sum := n1 + n2
return sum
}
func main(){
res := getSum(1, 2)
fmt.Println(res)
}
func 函数名(形参列表) 返回值{
主体
}
在使用时一定要区分好形参,实参的对应情况
下面结合指针来理解函数的工作原理
func add1(n int){
n += 2
}
func add2(n *int){
*n += 2
}
当我们调用函数,想实现给函数的参数+2的操作时候,add2是起作用的,add1不会对传入的参数产生任何变换。
为什么会这样呢?实际上,****add1是对实际参数的 拷 贝(重点) 加2,不会影响实际参数,而add2是在传入的实际参数的真正地址上操作,作用是实打实的,不会有 拷 贝 这一步骤
深层解释:如果不传地址,就会在stack新建一个空间,实际调用时候传递的参数的复印件就会在这个新空间发生改变,而需要改变的参数是之前就已经存在在stack里面的,函数作用完毕之后,对该参数没有任何影响。
这下解释清楚了吧,只要知道有拷贝这一作用就很好理解
用指针作为形式参数的好处:
- 可以对实际参数(也用在结构体上面)进行修改
- 避免一些大的参数(例如结构体,切片等)的拷贝开销
但也并非所有情况都适用用指针做形式参数,只有需要修改原值的时候我们才使用,在实际使用过程中要记得区分
返回值
Go的函数支持多个返回值,举例如下:
func getRes(n1 int, n2 int) (int, int) {
sum := n1 + n2
difference := n1 - n2
return sum, difference
}
两个及以上的返回值,上面的数据类型要加括号
也可以直接给返回值命名:
func getRes(n1, n2 int) (sum, difference int) {
sum = n1 + n2
difference = n1 - n2
return
}
函数也是一种数据类型
以下内容引自枫枫知道,文章开头附了链接:
函数不是变量,但他也存在与内存中,存在内存地址
函数名的本质就是一个指向其函数内存地址的指针常量
func getRes(n1, n2 int) (sum, difference int) {
sum = n1 + n2
difference = n1 - n2
return
}
func main() {
fmt.Printf("getRes is %v, type is %T", getRes, getRes)
//getRes is 0x4aca00, type is func(int, int) (int, int)
}
所以,我们也可以以声明变量的形式声明函数
var getRes = func(n1, n2 int) (sum, difference int) {
sum = n1 + n2
difference = n1 - n2
return
}
a, b := getRes(1, 2)
fmt.Println(a, b)
getRes 对应的那个函数也叫匿名函数
面向对象
类
重要声明:请路过的读者略读该部分,作者自己认为没有完全理解Go语言中类的地位,以及方法在Go语言中和类的关系,所以都写上来,请读者仔细分辨对错!
1、基本概念
对象
对象是由数据(描述事物的属性)和作用于数据的操作(体现事物的行为)组成的封装体,描述客观事物的一个实体,是构成系统的基本单元。
类
类是对一组有相同数据和相同操作的对象的定义,是对象的模板,其包含的方法和数据描述一组对象的共同行为和属性。类是在对象之上的抽象,对象则是类的具体化,是类的实例。类可有其子类,也可有其他类,形成类层次结构。
2、类与对象的区别
1)类是对象的抽象,而对象是类的具体实例。类是抽象的,不占用内存,而对象是具体的,占用存储空间。
2)类是一组具有相同属性和行为的对象的抽象。我们可以将类看做是创建对象蓝图,对象根据这个蓝图去具体实现某个东西。
总而言之:类的示例就是对象。对象必须基于类来创建;而类是抽象的模板,如果没有具体到一个对象上,类本身的属性和方法是没有意义的。
类和结构体还是有点类似的。在这里拿结构体举例:
结构体
由一组字段构成的一种自定义数据类型
因为和cpp中结构体类似,在这里不多赘述语法,贴一段代码,在必要时候再进行说明。
package main
import "fmt"
type User struct {
Name stringAge
intpassword string
}
type Account struct {
Usermoney float32
Name string
}
func main() {
var ac Account = Account{
money: 21.9,
User: User{
Name: "王五",
Age: 21,
password: "978345",
},
Name: "账户1",
}
fmt.Printf("ac %#v, %T\n", ac, ac)
var u1 = User{Name: "lisa",Age: 22,password: "1233fg", }
var ac1 Account = Account{money: 21.9,User: u1, }
fmt.Printf("ac1 %#v, %T\n",ac1,ac1)
fmt.Println(ac.money)
fmt.Println(ac.Name)
fmt.Println(ac.User.Name)
fmt.Println(ac.password)}`
方法
和函数没什么区别,只是多了一个形参,是和结构体绑定在一起的。在函数声明时,在其名字之前放上一个变量,即是一个方法。 这个附加的参数会将该函数附加到这种类型上,即相当于为这种类型定义了一个独占的方法。
package main
import "fmt"
type User struct {
Name string `json:"name"`
Id uint16 `json:"id"`
}
func (u User) PrintName() string {
return u.Name
}
func main() {
u := User{Name: "张三",Id: 1001, }
fmt.Println(u.PrintName())
}
我们都知道golang的函数都是值传递
所以如果在方法中修改结构体的值
相当于修改的是结构体的副本
如果需要修改原结构体,那么需要传递的是结构体的指针
type User struct {
Name string `json:"name"`
Id uint16 `json:"id"`
}
func (u *User) SetName(name string) {
(*u).Name = name // 也可这样简写 //u.Name = name
fmt.Printf("setName修改之后:u=%+v\n", *u)
}
func main() {
u := User{Name: "张三",Id: 1001, }
u.SetName("李四")
fmt.Printf("setName运行完之后,u=%+v\n", u)}