Go结构体(struct)

news2025/1/23 17:29:24

文章目录

  • Struct
    • 定义struct
    • 构造struct实例
    • struct的值和指针
    • 在与函数共用时:
    • 匿名字段和嵌套struct
    • 嵌套struct的名称冲突问题

Struct

是一个值类型的

定义struct

type identifier struct {
    field1 type1
    field2 type2
    …
}
// 或者
type T struct { a, b int }

理论上,每个字段都是有具有唯一性的名字的,但如果确定某个字段不会被使用,可以将其名称定义为空标识符_来丢弃掉:

type T struct {
    _ string
    a int
}

每个字段都有类型,可以是任意类型,包括内置简单数据类型、其它自定义的struct类型、当前struct类型本身、接口、函数、channel等等。

如果某几个字段类型相同,可以缩写在同一行:

type mytype struct {
    a,b int
    c string
}

构造struct实例

定义了struct,就表示定义了一个数据结构,或者说数据类型,也或者说定义了一个类。总而言之,定义了struct,就具备了成员属性,就可以作为一个抽象的模板,可以根据这个抽象模板生成具体的实例,也就是所谓的"对象"。

例如:

type person struct{
    name string
    age int
}

// 初始化一个person实例
var p person

这里的p就是一个具体的person实例,它根据抽象的模板person构造而出,具有具体的属性name和age的值,虽然初始化时它的各个字段都是0值。换句话说,p是一个具体的人。

struct初始化时,会做默认的赋0初始化,会给它的每个字段根据它们的数据类型赋予对应的0值。例如int类型是数值0,string类型是"",引用类型是nil等。

因为p已经是初始化person之后的实例了,它已经具备了实实在在存在的属性(即字段),所以可以直接访问它的各个属性。这里通过访问属性的方式p.FIELD为各个字段进行赋值。

// 为person实例的属性赋值,定义具体的person
p.name = "longshuai"
p.age = 23

获取某个属性的值:

fmt.Println(p.name) // 输出"longshuai"

也可以直接赋值定义struct的属性来生成struct的实例,它会根据值推断出p的类型。

var p = person{name:"longshuai",age:23}

p := person{name:"longshuai",age:23}

// 不给定名称赋值,必须按字段顺序
p := person{"longshuai",23}

p := person{age:23}
p.name = "longshuai"

如果struct的属性分行赋值,则必须不能省略每个字段后面的逗号",",否则就会报错。这为未来移除、添加属性都带来方便:

p := person{
    name:"longshuai",
    age:23,     // 这个逗号不能省略
}

除此之外,还可以使用new()函数或&TYPE{}的方式来构造struct实例,它会为struct分配内存,为各个字段做好默认的赋0初始化。它们是等价的,都返回数据对象的指针给变量,实际上&TYPE{}的底层会调用new()。

p := new(person)
p := &person{}

// 生成对象后,为属性赋值
p.name = "longshuai"
p.age = 23

使用&TYPE{}的方式也可以初始化赋值,但new()不行:

p := &person{
    name:"longshuai",
    age:23,
}

选择new()还是选择&TYPE{}的方式构造实例?完全随意,它们是等价的。但如果想要初始化时就赋值,可以考虑使用&TYPE{}的方式。

struct的值和指针

package main  
  
import (  
   "fmt"  
)  
  
type person struct {  
   name string  
   age  int  
}  
  
func main() {  
   p1 := person{}  
   p2 := &person{}  
   p3 := new(person)  
   fmt.Println(p1)  
   fmt.Println(p2)  
   fmt.Println(p3)  
}

输出:

{ 0}
&{ 0}
&{ 0}

可见p1,p2,p3是不一样的
p1、p2、p3都是person struct的实例,但p2和p3是完全等价的,它们都指向实例的指针,指针中保存的是实例的地址,所以指针再指向实例,p1则是直接指向实例。这三个变量与person struct实例的指向关系如下:

变量名      指针     数据对象(实例)
-------------------------------
p1(addr) -------------> { 0}
p2 -----> ptr(addr) --> { 0}
p3 -----> ptr(addr) --> { 0}

p1和ptr(addr)保存的都是数据对象的地址,p2,p3则保存着ptr的地址。
不过要注意,p1.name和p2.name都可以访问实例的属性.

再看看

var p4 *person

这样的定义也是一个指针,他指向person的对象,初始化是nil,即没有明确的方向。
图示:

p4 -> ptr(nil)

既然p4是一个指针,那么可以将&person{}new(person)赋值给p4。

var p4 *person
p4 = &person{
    name:"longshuai",
    age:23,
}
fmt.Println(p4) 

上面的代码将输出:

&{longshuai 23}

在与函数共用时:

因为函数值传递时,是用的copy的方法,耗内存 所以可以直接传指针。
例如:

func add(p *person){...}

既然要传指针,那struct的指针何来?自然是通过&符号来获取。分两种情况,创建成功和尚未创建的实例。

对于已经创建成功的struct实例p,如果这个实例是一个值而非指针(即p->{person_fields}),那么可以&p来获取这个已存在的实例的指针,然后传递给函数,如add(&p)

匿名字段和嵌套struct

struct中的字段可以不用给名称,这时称为匿名字段。匿名字段的名称强制和类型相同。例如:

type animal struct {
    name string
    age int
}
type Horse struct{
    int
    animal //匿名字段
    sound string
}

上面的Horse中有两个匿名字段intanimal,它的名称和类型都是int和animal。等价于:

type Horse struct{
    int int
    animal animal
    sound string
}

显然,上面Horse中嵌套了其它的struct(如animal)。其中animal称为内部struct,Horse称为外部struct。

以下是一个嵌套struct的简单示例:

package main

import (
    "fmt"
)

type inner struct {
    in1 int
    in2 int
}

type outer struct {
    ou1 int
    ou2 int
    int
    inner
}

func main() {
    o := new(outer)
    o.ou1 = 1
    o.ou2 = 2
    o.int = 3
    o.in1 = 4
    o.in2 = 5
    fmt.Println(o.ou1)  // 1
    fmt.Println(o.ou2)  // 2
    fmt.Println(o.int)  // 3
    fmt.Println(o.in1)  // 4
    fmt.Println(o.in2)  // 5
}

上面的o是outer struct的实例,但o除了具有自己的显式字段ou1和ou2,还具备int字段和inner字段,它们都是嵌套字段。一被嵌套,内部struct的属性也将被外部struct获取,所以o.into.in1o.in2都属于o。也就是说,外部struct has a 内部struct,或者称为struct has a field

上面的outer实例,也可以直接赋值构建:

o := outer{1,2,3,inner{4,5}}

在赋值inner中的in1和in2时不能少了inner{},否则会认为in1、in2是直接属于outer,而非嵌套属于outer。

嵌套struct的名称冲突问题

假如外部struct中的字段名和内部struct的字段名相同,会如何?

有以下两个名称冲突的规则:

  1. 外部struct覆盖内部struct的同名字段、同名方法
  2. 同级别的struct出现同名字段、方法将报错
package main  
  
import "fmt"  
  
type A struct {  
   a int  
   b int  
}  
  
type B struct {  
   b float32  
   c string  
   d string  
}  
  
type C struct {  
   A  
   B   a string  
   c string  
}  
  
func main() {  
   var c C  
   c.a = "ca"  
   fmt.Println(c.a)  
}

按照规则(1),直属于C的a和c会分别覆盖A.a和B.c。可以直接使用c.a、c.c分别访问直属于C中的a、c字段,使用c.d或c.B.d都访问属于嵌套的B.d字段。如果想要访问内部struct中被覆盖的属性,可以c.A.a的方式访问。

按照规则(2),A和B在C中是同级别的嵌套结构,所以A.b和B.b是冲突的,将会报错,因为当调用c.b的时候不知道调用的是c.A.b还是c.B.b。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ygXR27pi-1673266569990)(../images/Pasted%20image%2020230109194508.png)]

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/151336.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JAVA多线程初阶(1)

目录JAVA多线程(1)1.Thread类创建与使用1.1 继承Thread类1.2 实现并发关于sleep()1.3 Runnable创建线程1.4 匿名内部类创建线程1.5 lamda表达式创建线程2.多线程提高效率3.Thread类属性和方法3.1 Thread(String name)3.2 isDaemon()3.3 isAlive()3.3 线程的重要方法3.4 中断线程…

数据结构:图

文章目录图内存中存储图数据结构邻接矩阵存储方法用邻接矩阵(Adjacency Matrix)来表示一个图的缺点:浪费空间优点邻接表存储方法(Adjacency List)广度优先算法Breadth-First-Search(BFS)深度优先…

Android——GT库-日志工具

GT库在创造出来初期,里面的日志工具就一直存在的,经历了很久的迭代变更,当目前的最新版本,日志工具已经创造出更高级的调试日志方式了,接下来咋们来看看GT库中的日志工具具体使用方法吧。 使用GT库里的,当然…

web表单设计器的优点体现在哪?

在数字化管理越来越规范的当下,拥有一款优质高效的低代码开发平台,确实能给企业提质增效带来更大的帮助。很多客户朋友会问道:web表单设计器都有哪些特点?为什么能在企业的现代化办公管理中起到巨大的作用?今天&#x…

Linux终端远程工具xshell,xftp,mobasterm

目录 软件介绍 1.xshell 第一步: 第二步: 第三步: 第四步: 第5步: 2.xftp 第一步: 第二部: 第三步: 3.mobasterm 全能终端神器——MobaXterm 第一步: 第二步&a…

C1083无法打开包括文件: “atlbase.h”: No such file or directory

在打开别人的项目的过程中遇到了“atlbase.h”无法打开的问题,在此记录一下。1.下载ATL生成工具与缓解只下载ATL生成工具后面还会报错,直接下载下载ATL生成工具与缓解一步到位。下载的入口在:工具--->获取工具与功能。需要注意的是&#x…

Guitar Pro2023Win/Mac中文吉他/贝斯打谱识谱软件

Guitar Pro 是一款曲谱阅读器。以 GTP 结尾的曲谱文件都必须用 Guitar Pro 才能打开。Guitar Pro 凭借着其便利的制谱和读曲谱环境,在各大谱库论坛里都占据着一席之地,喜欢吉他的朋友一定略有耳闻。早几年该作者将它移植到了移动平台,现在你也…

7-2国王游戏

题目: 恰逢 H 国国庆,国王邀请 n 位大臣来玩一个有奖游戏。 首先,他让每个大臣在左、右手上面分别写下一个整数,国王自己也在左、右手上各写一个整数。 然后,让这 n 位大臣排成一排,国王站在队伍的最前面。…

应用层——Web和HTTP

目录 1. HTTP概况 1.1 Web页面简介 1.2 URL-统一资源定位器 1.3 HTTP协议 2. HTTP连接的两种类型 2.1 HTTP非持久性连接(Non-persistent HTTP) 2.2 HTTP持久性连接(Persistent HTTP) 2.2.1 无流水(pipelining)的持久性连接 2.2.2 带有流水机制的持久性连接 3. HT…

一站式开发平台赋能办公全场景

近几年,数字化办公迎来了新的机遇,根据亿欧智库《2022中国数字化办公市场研究报告》推算,数字化办公2021年的市场规模达到973.89亿元,至2025年将达到1768.16亿元,整体增速保持平稳,2018-2025年的CAGR为15.8…

Mybatis 框架搭建封装JDBC,实现sql语句

目录 1、maven新建一个工程​编辑 2、添加POM.XML配置文件 3、创建实例包 4、创建一个环境资源根目录 5、配置环境文件 6、创建接口,添加方法 7、编写sql语句 8、创建测试类 8.1 、定义工厂模式 8.2 、定义会话 8.3、定义对象 8.5、获取Builder建造工厂 …

LAB3 EIGRP1实验

1 实验拓扑: 2 实验要求: 1>.R1-R3环回口0:192.168.100.x/32。 2>.R1上采用手动汇总的命令,汇总4条环回口成一条。 3>.R1上下发一条默认路由。 4>.实现R1到R2的环回口路由非等价负载。 5>.as 90都使用eigrp认证。 6>…

css动画效果之transition

transition-property规定设置过渡效果的 CSS 属性的名称。属性名属性值none没有属性会获得过渡效果。all所有属性都将获得过渡效果。property定义应用过渡效果的 CSS 属性名称列表,列表以逗号分隔。使用方式transition-property: width,background;/* 多个效果可用逗…

设计模式之装饰模式

1.前言 装饰模式:动态的给一个类添加一些额外职责,就增加功能来说,装饰模式比生成子类更加灵活。 装饰模式属于结构型模式,它是作为现有的 类的⼀个包装,允许向⼀个现有的对象添加新的功能, 同时⼜不改变其…

Spring创建和使用 (存储和读取) -- 1

Spring创建和使用 存储和读取 -- 1一、创建 Spring 项目1.1 创建⼀个 Maven 项目1.2 添加 Spring 框架支持1.3 添加启动类二、存储 Bean 对象2.1 创建 Bean2.2 将 Bean 注册到容器三、获取并使用 Bean 对象3.1 创建 Spring 上下文3.2 获取指定的 Bean 对象3.3 使用 Bean四、总结…

数据结构-第六期——并查集(Python)

目录 认识并查集 经典应用: 应用场景 并查集的操作 初始化 代码实现 合并 代码实现 查找 代码实现 查找代码【图解】 有多少个集(帮派)? 复杂度 查询的优化:路径压缩 【代码】用递归实现 并查集:初始化、查找、合并代码 蓝桥杯…

ES6之Promise

Promise是异步操作的一种解决方案 // 1.认识Promisedocument.addEventListener(click,()>{console.log(这里是异步的);});console.log(这里是同步的); Promise一般用来解决层层嵌套的回调函数&#xff08;回调地狱&#xff09;的问题 <!DOCTYPE html> <html lan…

JVM垃圾回收机制、JVM垃圾回收算法、JVM CMS与G1垃圾收集,JVM内存模型

C C 需要自己回收垃圾 重复回收&#xff1a; 回收掉别人的东西 忘记回收&#xff1a; 内存泄漏 Java虚拟机做自动化回收 垃圾回收器 Root Searching&#xff08;根可达&#xff09; GC Algorithms(垃圾回收算法) Mark-Sweep(标记清除) 缺点&#xff1a;碎片化&#xff0c;一…

Lua C接口编程(一)

引言 skynet 和 openresty 都是深度使用lua的典范&#xff0c;学习lua不经要学会基本语法&#xff0c;还要学会C语言与Lua交互。lua的一大优点就是能和c/c无缝连接&#xff0c;而且可以在不需要重复编译c/c的情况下可以修改lua文件并且起作用&#xff0c;当我们的项目文件很大…

【面试题】做了一份前端面试复习计划,保熟~

大厂面试题分享 面试题库前端面试题库 &#xff08;面试必备&#xff09; 推荐&#xff1a;★★★★★地址&#xff1a;前端面试题库前言以前我看到面试贴就直接刷掉的&#xff0c;从不会多看一眼&#xff0c;直到去年 9 月份我开始准备面试时&#xff0c;才发现很多面试经验贴…