一文带你了解Go语言中的指针和结构体

作者:陈明勇 时间:2024-04-25 15:26:20 

前言

前面的两篇文章对 Go 语言的基础语法和基本数据类型以及几个复合数据类型进行介绍,本文将对 Go 里面的指针和结构体进行介绍,也为后续文章做铺垫。

指针

Go 语言中,指针可以简单理解是一个地址,指针类型是依托于某一个类型而存在的,例如 Go 里面的基本数据类型 intfloat64string 等,它们所对应的指针类型为 *int*float64*string等。

指针的定义

语法格式:var 指针变量名 *数据类型 = &变量

& 为取地址符号,通过 & 符号获取某个变量的地址,然后赋值给指针变量。

import (
"fmt"
)

func main() {
var num int = 666
var numPtr *int = &num
fmt.Println(numPtr)  // num 变量的地址值 0xc00001c098
fmt.Println(&numPtr) // 指针变量本身的地址值 0xc00000a028
}

npmPtr 指针变量指向变量 num0xc00001c098num 变量的地址,0xc00000a028 为指针变量本身的地址值。

一文带你了解Go语言中的指针和结构体

使用 new(T) 函数创建指针变量

import (
"fmt"
)

func main() {
numPtr := new(int)
fmt.Println(numPtr)  // 0xc000122058
fmt.Println(*numPtr) // 0
}

new(T) 函数为每个类型分配一片内存,内存单元保存的是对应类型的零值,函数返回一个指针类型。

错误的类型地址赋值

func main() {
var num float64 = 666
var numPtr *int = &num // cannot use &num (value of type *float64) as type *int in variable declaration
}

当指针所依托的类型与变量的类型不一致时,Go 编译器会报错,类型不匹配。

获取和修改指针所指向变量的值

通过指针获取所指向变量的值

func main() {
var num int = 666
var numPtr *int = &num

// 获取 num 的值
fmt.Println(*numPtr) // 666
}

对指针使用 * 操作符可以获取所指向变量的值。

通过指针修改所指向变量的值

import (
"fmt"
)

func main() {
var num int = 666
var numPtr *int = &num

// 修改 num 的值
*numPtr = 555
fmt.Println(num) // 555
}

同时也可以对指针使用 * 操作符修改所指向变量的值。

结构体

通过上一篇文章,我们了解了数组和切片的特点,它们可以存储一组相同类型的数据,而结构体,它可以由 0 个或多个字段组成,每个字段的数据类型可以一样,也可以不一样。结构体可以表示一个对象,例如:人,人包含的一些字段有姓名、性别和年龄等。

结构体定义

语法格式:

type XXX struct {
/*
结构体字段定义区域
*/
}

XXX 为结构体的名字,下面以人为对象,引入结构体

// Person定义一个人的结构体
type Person struct {
// 姓名
Name string
// 年龄
Age int
// 性别
Sex string
// 身份证号
idNumber string
}

上述代码定义了人的结构体 Person,包含四个字段,字段的命名规则和变量是一样的,前三个字段首字母大写,可以被包外访问,第四个字段首字母小写,表示只能在包内访问。 除了上述的定义方式以外,结构体里还可以内嵌结构体

// Person 定义一个人的结构体
type Person struct {
// 姓名
Name string
// 年龄
Age int
// 性别
Sex string
// 身份证号
idNumber string
}

// Phone 手机结构体
type Phone struct {
// 品牌
Brand string
// 拥有者
Owner Person
}

上述代码定义了 Person 结构体和 Phone 结构体,Phone 结构体拥有两个字段,分别为 Brand 品牌和 Owner 拥有者,Owner 属性的类型,指定为我们所自定义的 Person 结构体。以这种方式定义的结构体字段,我们可以称为嵌入字段(Embedded Field)。也可以将这种字段称为匿名字段。

结构体的创建方式

1、声明一个结构体变量

// Person 定义一个人的结构体
type Person struct {
// 姓名
Name string
// 年龄
Age int
// 性别
Sex string
// 身份证号
idNumber string
}

func main() {
var person Person
fmt.Println(person.Name)     // ""
fmt.Println(person.Age)      // 0
fmt.Println(person.Sex)      // ""
fmt.Println(person.idNumber) // ""

// 修改结构体字段的值
person.Name = "chenmingyong"
fmt.Println(person.Name) // "chenmingyong"
}

通过声明一个结构体变量,隐式创建一个结构体变量,这个结构体变量各个字段值默认为零值,也就是对应类型的默认值。

结构体属性的值,可以通过 变量.字段名 的方式获取,同时也可以通过此方式对字段值进行修改。

2、使用复合字面值进行显式初始化结构体对象

import "fmt"

// Person 定义一个人的结构体
type Person struct {
// 姓名
Name string
// 年龄
Age int
// 性别
Sex string
// 身份证号
idNumber string
}

func main() {
person := Person{
"chenmingyong",
18,
"男",
"xxxxxxxxx",
}

fmt.Println(person) // {chenmingyong 18 男 xxxxxxxxx}
}

上述代码根据字段的顺序进行赋值,实际上,Go 语言并不推荐我们用这种方式进行赋值,因为可能会带来一些问题,例如结构体的某个中间的字段被删除或者字段的顺序改变了,那么我们需要维护对应代码,调整赋值的顺序。

上述创建结构体的方式有弊端,因此我们需要换一种方法,如下第三种方法。

3、 使用 field:value 形式的复合字面值进行显式初始化结构体对象

import "fmt"

// Person 定义一个人的结构体
type Person struct {
// 姓名
Name string
// 年龄
Age int
// 性别
Sex string
// 身份证号
idNumber string
}

func main() {
person := Person{
   Sex:      "男",
   Age:      18,
   Name:     "chenmingyong",
   idNumber: "xxxxxxxxx",
   }

fmt.Println(person) // {chenmingyong 18 男 xxxxxxxxx}
}

通过以上的方式,我们就不被字段的顺序所约束了。

4、通过 new(T) 函数创建结构体指针

// Person 定义一个人的结构体
type Person struct {
// 姓名
Name string
// 年龄
Age int
// 性别
Sex string
// 身份证号
idNumber string
}

func main() {
person := new(Person)
(*person).Name = "chenmignyong"
fmt.Println((*person).Name) // chenmignyong
// 简化赋值,底层自动转换成 (*person).Age = 18
person.Age = 18
fmt.Println(person.Age) // 18
}

前面提到过,访问指针所指向变量的值,需要使用 * 操作符,但在结构体这里有点特殊,如果不加 * 的话,底层会将 person.Age = 18 转成 (*person).Age = 18

小结

本文对指针和结构体进行了介绍,也指出使用指针和结构体时需要注意的一些地方。因为本文是基于了解的层面去写的文章,一些深入的知识并没有提到,然后也没有提到结构体的方法,是因为打算留到后面和函数一起去介绍。

来源:https://juejin.cn/post/7169961029328175112

标签:Go语言,指针,结构体
0
投稿

猜你喜欢

  • 解决vue单页面多个组件嵌套监听浏览器窗口变化问题

    2024-04-27 15:48:29
  • mysql根据json字段内容作为查询条件(包括json数组)检索数据

    2024-01-13 00:50:50
  • 利用vue实现打印页面的几种方法总结

    2023-07-02 17:09:48
  • 一分钟学会JavaScript中的try-catch

    2024-05-10 14:06:57
  • DreamweaverMX 2004打造细线表格

    2008-10-01 09:39:00
  • python使用tkinter实现简单计算器

    2021-10-01 00:20:57
  • Jupyter Notebook运行代码无反应问题及解决方法

    2023-07-21 17:38:04
  • 浅谈keras使用中val_acc和acc值不同步的思考

    2023-03-14 18:29:45
  • IE和Firefox的js兼容性整理

    2007-11-21 19:40:00
  • opencv中cv2.minAreaRect函数输出角度问题详解

    2023-09-08 06:38:47
  • Python获取Linux系统下的本机IP地址代码分享

    2021-07-23 00:26:22
  • JavaScript实现QQ聊天室功能

    2024-04-19 09:47:53
  • ASP进阶学习之认识数学函数

    2007-10-08 13:15:00
  • wxPython框架类和面板类的使用实例

    2022-06-29 08:16:27
  • python自动查询12306余票并发送邮箱提醒脚本

    2023-08-26 09:34:37
  • MySql的事务使用与示例详解

    2024-01-27 17:24:50
  • 运用Python巧妙处理Word文档的方法详解

    2023-11-13 16:58:29
  • python添加列表元素append(),extend()及 insert()

    2021-03-07 06:17:50
  • python实现根据月份和日期得到星座的方法

    2023-02-02 01:05:51
  • Python中openpyxl实现vlookup函数的实例

    2022-09-28 10:22:26
  • asp之家 网络编程 m.aspxhome.com