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

目录
  • 前言
  • 指针
    • 指针的定义
    • 获取和修改指针所指向变量的值
  • 结构体
    • 结构体定义
    • 结构体的创建方式
  • 小结

前言

前面的两篇文章对 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 为指针变量本身的地址值。

使用 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

小结

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

到此这篇关于一文带你了解Go语言中的指针和结构体的文章就介绍到这了,更多相关Go语言指针 结构体内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Go语言中结构体方法副本传参与指针传参的区别介绍

    GO语言结构体方法跟结构体指针方法的区别 首先,我定了三个接口.一个结构和三个方法: type DeptModeA interface { Name() string SetName(name string) } type DeptModeB interface { Relocate(building string, floor uint8) } type Dept struct { name string building string floor uint8 Key string } fun

  • go语言结构体指针操作示例详解

    目录 指针 go指针操作 不能操作不合法指向 new函数 指针做函数的参数 数组指针 结构体指针变量 结构体成员普通变量 结构体成员指针变量 结构体比较和赋值 结构体作为函数参数 指针 指针是代表某个内存地址的值.内存地址储存另一个变量的值. 指针(地址),一旦定义了不可改变,指针指向的值可以改变 go指针操作 1.默认值nil,没有NULL常量 2.操作符“&”取变量地址,“*“通过指针(地址)访问目标对象(指向值) 3.不支持指针运算,不支持“->”(箭头)运算符,直接用“.”访问目标成

  • Go语言指针访问结构体的方法

    本文实例讲述了Go语言指针访问结构体的方法.分享给大家供大家参考.具体分析如下: Go有指针,但是没有指针运算. 结构体字段可以通过结构体指针来访问.通过指针间接的访问是透明的. 复制代码 代码如下: package main import "fmt" type Vertex struct {     X int     Y int } func main() {     p := Vertex{1, 2}     q := &p     q.X = 1e9     fmt.P

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

    目录 前言 指针 指针的定义 获取和修改指针所指向变量的值 结构体 结构体定义 结构体的创建方式 小结 前言 前面的两篇文章对 Go 语言的基础语法和基本数据类型以及几个复合数据类型进行介绍,本文将对 Go 里面的指针和结构体进行介绍,也为后续文章做铺垫. 指针 在 Go 语言中,指针可以简单理解是一个地址,指针类型是依托于某一个类型而存在的,例如 Go 里面的基本数据类型 int.float64.string 等,它们所对应的指针类型为 *int.*float64.*string等. 指针的定

  • 一文带你了解Go语言中的单元测试

    目录 基本概念 示例一:取整函数基本测试 示例二:Fail()函数 示例三:FailNow函数 实例四:Log和Fetal函数 基本概念 上一节提到,代码完成的标准之一还包含了单元测试,这部分也是很多开发流程中不规范的地方.写过单元测试的开发人员应该理解,单元测试最核心的价值是为了证明:为什么我写的代码是正确的?也就是从逻辑角度帮你检查你的代码.但是另外一方面,如果从单元测试覆盖率角度来看,单元测试也是非常耗时的,几乎是三倍于你代码的开发时间,所以在很多迭代速度非常快的项目中,单元测试就几乎没人

  • 一文带你入门Go语言中定时任务库Cron的使用

    目录 前言 快速开始 安装 导入 Demo Cron表达式格式 标准格式 预定义时间表 常用的方法介绍 new() AddJob() AddFunc() Start() 相关推荐 Go第三方库之cronexpr——解析 crontab 表达式 总结 前言 在平时的开发需求中,我们经常会有一些重复执行的操作需要触发执行,和系统约个时间,在几点几分几秒或者每隔几分钟跑一个任务,说白了就是定时任务,,想必大家第一反应都是linux的Crontab.其实定时任务不止使用系统自带的Crontab,在Go语

  • 一文带你了解Go语言中的类型断言和类型转换

    目录 类型断言 类型判断 为什么需要断言 类型转换 什么时候使用类型转换 类型为什么称为转换 类型结论 在Go中,类型断言和类型转换是一个令人困惑的事情,他们似乎都在做同样的事情. 下面是一个类型断言的例子: var greeting interface{} = "hello world" greetingStr := greeting.(string) 接着看一个类型转换的例子: greeting := []byte("hello world") greeting

  • 一文带你了解Go语言中接口的使用

    目录 接口 接口的实现 接口类型变量 空接口 类型断言 类型断言变种 type switch 小结 接口 在 Go 语言中,接口是一种抽象的类型,是一组方法的集合.接口存在的目的是定义规范,而规范的细节由其他对象去实现.我们来看一个例子: import "fmt" type Person struct { Name string } func main() { person := Person{Name: "cmy"} fmt.Println(person) //

  • 一文带你掌握Go语言中的文件读取操作

    目录 os 包 和 bufio 包 os.Open 与 os.OpenFile 以及 File.Read 读取文件操作 bufio.NewReader 和 Reader.ReadString 读取文件操作 小结 os 包 和 bufio 包 Go 标准库的 os 包,为我们提供很多操作文件的函数,如 Open(name) 打开文件.Create(name) 创建文件等函数,与之对应的是 bufio 包,os 包是直接对磁盘进行操作的,而 bufio 包则是带有缓冲的操作,不用每次都去操作磁盘.

  • 一文带你了解Go语言中方法的调用

    目录 前言 方法 方法的调用 Receiver 参数类型的选择 方法的约束 小结 前言 在前面的 一文熟悉 Go 函数 文章中,介绍了 Go 函数的声明,函数的几种形式如匿名函数.闭包.基于函数的自定义类型和函数参数详解等,而本文将对方法进行介绍,方法的本质就是函数,介绍方法的同时也会顺带对比其与函数的不同之处. 方法 在 Go 中,我们可以为任何的数据类型定义方法(指针或接口除外),现在让我们看一看方法的声明和组成部分以及与函数有什么不同之处. type Person struct { age

  • 一文带你熟悉Go语言中函数的使用

    目录 函数 函数的声明 Go 函数支持变长参数 匿名函数 闭包 init 函数 函数参数详解 形式参数与实际参数 值传递 函数是一种数据类型 小结 函数 函数的英文单词是 Function,这个单词还有着功能的意思.在 Go 语言中,函数是实现某一特定功能的代码块.函数代表着某个功能,可以在同一个地方多次使用,也可以在不同地方使用.因此使用函数,可以提高代码的复用性,减少代码的冗余. 函数的声明 通过案例了解函数的声明有哪几部分: 定义一个函数,实现两个数相加的功能,并将相加之后的结果返回. f

  • 一文带你熟悉Go语言中的分支结构

    目录 分支结构 if 单分支 if 双分支 if-else 多分支 if - else if - else 在 if 语句中声明变量 switch 示例 switch 分支当 if 分支使用 在 switch 语句中声明变量 fallthrough 小结 分支结构 分支结构是结构化程序设计中的基础.针对分支结构,Go 提供了两种语句形式,一种是 if,另一种是 switch. if if 语句是 Go 中最常用.最简单的分支控制结构,它分为单分支.双分支以及多分支三种用法.if 语句会根据布尔变

  • 一文带你了解C语言中的动态内存管理函数

    目录 1.什么是动态内存管理 2.为什么要有动态内存管理 3.如何进行动态内存管理 3.1 malloc 3.2 free 3.3 calloc 3.4 realloc 总结 1.什么是动态内存管理 平时我们写代码,一种非常常见的写法是: int a = 0; // 创建一个变量 int arr[10] = {0}; // 创建一个数组 当你创建变量a的时候,其实是向内存中申请了4个字节的空间来存放一个整数.而当你创建数组arr的时候,是向内存中申请了40个字节的空间来存放10个整数.当你这么写

随机推荐