Go语言语法基础入门(下) | 青训营

103 阅读6分钟

# 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里面的,函数作用完毕之后,对该参数没有任何影响。


这下解释清楚了吧,只要知道有拷贝这一作用就很好理解

用指针作为形式参数的好处:

  1. 可以对实际参数(也用在结构体上面)进行修改
  2. 避免一些大的参数(例如结构体,切片等)的拷贝开销

但也并非所有情况都适用用指针做形式参数,只有需要修改原值的时候我们才使用,在实际使用过程中要记得区分

返回值

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)}