Go语言入门系列前面的文章:

1. 指针

如果你使用过C或C++,那你肯定对指针这个概念不陌生。

我们需要先介绍两个概念:内存和地址。

1.1. 内存和地址

我们写的代码都存储在外存(C盘、D盘)中,比如我存在了D:\Work\Program\go目录下。如果你想要运行你的代码,必须先把你的代码加载进内存中,然后交给CPU执行计算,而CPU计算的结果也会存到内存中。

内存的存取速度快,其中有许多存储单元用来存储数据,CPU能在内存中直接找到这些数据。这是因为内存中的每个位置都有一个独一无二的地址标识。可以把内存看成一幢有许多房间的大楼,每个存储单元是一个房间,存储的数据是房间中的物品,地址就是房间号。

所以对CPU来说,如果想找到某个房间中的物品(从内存中取数据),或者向某个房间中放物品(向内存中存数据),我们必须知道房间号(内存地址)。

内存地址通常是一串16进制的数字,如果写代码时存个整数1或取个整数1都需要写这么一串数字,那太麻烦了。所以高级语言为我们提供了一个便利,用我们人能记住的“名字”来代替这串数字。

这些“名字”就是变量名

var a int = 1
var b int = 2
var c int = 333
var d int = 6666

变量名和地址的关联由编译器替我们做,硬件访问的仍然是内存地址。

1.2. 什么是指针?

简单地来说,指针也是一个变量,只不过这个变量中存的不是我们平常用到的1、2、3、"Hello"、true等值,而是其他变量的地址。

之所以取名指针,是因为指针变量b中保存了变量a的地址,我们可以通过该指针变量b找到变量a,如果画图看起来,看起来就像是指针b指向了变量a

还可以有指针的指针:

1.3. 指针的使用

声明一个指针:

var p *int

*int表示p是一个int类型指针,p指针中存的是一个int类型变量的地址,这意味着p中不能存其他类型变量的地址。

如何获取某个变量的地址呢?使用操作符&

var a int = 66 //a是值为66的int变量
p = &a //将a的地址赋给指针p

那么如何根据指针中的地址找到对应的变量呢?使用操作符*

var b = *p //根据p中的值找到a,将其值赋给b
fmt.Println(b) //66 *p = 99 //根据p中的值找到a,改变a的值
fmt.Println(a) //99

一定要注意指针的初始化,如果不初始化,则指针的的值是其零值——nil。对未初始化的指针赋值,则会出问题:

var p *int //只声明,未初始化
*p = 12 //报错:invalid memory address or nil pointer dereference

原因是指针p中没值,是个nil,自然就无法根据地址找到变量。如果你想使用指针,必须先确保你的指针中有合法的内存地址才行。应当这样写:

var a int
var p *int = &a //p被初始化为a的地址
*p = 12 //根据p的值找到a,12赋值给a
//或者
var a int
var p *int
p = &a //a的地址赋给p
*p = 12 //根据p的值找到a,12赋值给a

下面是一个完整的例子:

package main

import "fmt"

func main() {
var a int = 66 //变量a
var p *int = &a //指针p指向a
var b = *p //获取p指向的变量a的值
fmt.Println("a =",a, ", b =", b, ", p =", p)
fmt.Println("a的地址 =", &a, ", b的地址 =", &b, ", p的地址 =", &p) *p = 12 //改变p指向的变量a的值 fmt.Println("a =",a, ", b =", b, ", p =", p)
fmt.Println("a的地址 =", &a, ", b的地址 =", &b, ", p的地址 =", &p) var pp **int = &p //指针pp指向指针p
var c = *pp //获取pp指向的p的值
var d = **pp //获取pp指向的p指向的a的值
fmt.Println("pp =", pp, ", c =", c, ", d =", d)
fmt.Println("pp的地址 =", &pp, ", c的地址 =", &c, ", d的地址 =", &d)
}

2. 结构体 (struct)

2.1. 基本使用

和C语言一样,Go语言中也有结构体。

结构体就是一组字段/属性的集合。有了结构体,我们可以根据自己的需求定义自己的类型。比如狗,肯定不能用基本数据类型来表示,因为狗身上有许多属性:string类型的姓名、int类型的年龄等等,狗是一个拥有许多属性的集合,换句话说,狗是一个结构体。我们可以定义一个dog类型的结构体来表示狗。

结构体的声明方式:

type 结构体名字 struct {
字段名1 类型1
字段名2 类型2
...
}

下面是结构体dog的声明:

type dog struct {
name string
age int
}

声明了结构体后,就可以使用它。

首先,只要你正确声明了结构体后,你就能像使用intstring等基本类型声明变量一样去声明dog类型的变量,然后,你就能给声明的变量d的字段赋值了,通过点号.来访问结构体的字段

var d dog //声明一个dog类型的变量d
d.name = "哮天犬"
d.age = 3

除此之外,还有几种声明方式。

你可以按照字段顺序直接赋值

d := dog{"哮天犬", 3}

或者指定字段赋值,这样可以忽略字段顺序:

d := dog{age:3, name:"哮天犬"}

下面是一个完整的例子:

package main

import "fmt"

type dog struct {
name string
age int
} func main() {
var d dog //声明一个dog类型的变量d
d.name = "哮天犬"
d.age = 3 d1 := dog{"哮地犬", 2} d2 := dog{age:4, name:"哮人犬"} fmt.Println(d, d1, d2)
}

2.2. 结构体指针

我们可以获取结构体的指针:

d := dog{"哮地犬", 2}
p := &d //获取到d的地址

可以根据结构体指针访问其字段:

n := (*p).name
fmt.Println(n) //哮天犬

这种方式比较麻烦,Go语言提供了隐式间接引用:

n := p.name //这样也行
fmt.Println(n)

我们可以通过new函数给结构体分配一个指针。

先介绍一下new函数:new函数用于给各种类型的内存分配。new(T)会给T类型分配对其合适的内存空间,用T类型的零值填充,并返回其地址,是一个*T类型的值。换句话说,该函数会返回一个指向T类型零值的指针。

p := new(dog)
fmt.Printf("%T\n", p) //*main.dog
fmt.Println(p) //&{ 0}
fmt.Println(*p) //{ 0}

从上面打印的三行语句中也可以看出,new(dog)返回的是一个指针。

2.3. 结构体嵌套

一个结构体也可以作为另一个结构体的字段,下面是一个例子:

package main

import "fmt"

type people struct {
name string
age int
d dog
} type dog struct {
name string
age int
} func main() {
a := people{"行小观", 18, dog{"小狗", 2}}
fmt.Println(a) //{行小观 18 {小狗 2}}
fmt.Println(a.d) //{小狗 2}
fmt.Println(a.name) //行小观
fmt.Println(a.d.name) //小狗
}

也可以使用匿名字段,何为匿名字段?顾名思义,只提供类型,不写字段名:

package main

import "fmt"

type people struct {
name string
age int
dog //匿名字段
} type dog struct {
name string
age int
} func main() {
a := people{"行小观", 18, dog{"小狗", 2}}
fmt.Println(a) //{行小观 18 {小狗 2}}
fmt.Println(a.dog) //{小狗 2}
fmt.Println(a.name) //行小观
fmt.Println(a.dog.name) //小狗
}

作者简介

我是「行小观」,于千万人中的一个普通人。阴差阳错地走上了编程这条路,既然走上了这条路,那么我会尽可能远地走下去。

我会在公众号『行人观学』中持续更新「Java」、「Go」、「数据结构和算法」、「计算机基础」等相关文章。

欢迎关注,我们一起踏上行程。

本文章属于系列文章《Go语言入门系列》。

如有错误,还请指正。

Go语言入门系列(五)之指针和结构体的使用的更多相关文章

  1. Go语言入门系列(六)之再探函数

    Go语言入门系列前面的文章: Go语言入门系列(三)之数组和切片 Go语言入门系列(四)之map的使用 Go语言入门系列(五)之指针和结构体的使用 在Go语言入门系列(二)之基础语法总结这篇文章中已经 ...

  2. 【Go语言入门系列】(七)如何使用Go的方法?

    [Go语言入门系列]前面的文章: [Go语言入门系列](四)之map的使用 [Go语言入门系列](五)之指针和结构体的使用 [Go语言入门系列](六)之再探函数 本文介绍Go语言的方法的使用. 1. ...

  3. 【Go语言入门系列】(八)Go语言是不是面向对象语言?

    [Go语言入门系列]前面的文章: [Go语言入门系列](五)指针和结构体的使用 [Go语言入门系列](六)再探函数 [Go语言入门系列](七)如何使用Go的方法? 1. Go是面向对象的语言吗? 在[ ...

  4. C语言高速入门系列(五)

    C语言高速入门系列(五) C语言指针初涉                                           ------转载请注明出处:coder-pig 本节引言: 上一节我们对C ...

  5. Android For JNI(五)——C语言多级指针,结构体,联合体,枚举,自定义类型

    Android For JNI(五)--C语言多级指针,结构体,联合体,枚举,自定义类型 我们的C已经渐渐的步入正轨了,基础过去之后,就是我们的NDK和JNI实战了 一.多级指针 指针的概念我们在前面 ...

  6. Go语言入门系列(四)之map的使用

    本系列前面的文章: Go语言入门系列(一)之Go的安装和使用 Go语言入门系列(二)之基础语法总结 Go语言入门系列(三)之数组和切片 1. 声明 map是一种映射,可以将键(key)映射到值(val ...

  7. 【Go语言入门系列】(九)写这些就是为了搞懂怎么用接口

    [Go语言入门系列]前面的文章: [Go语言入门系列](六)再探函数 [Go语言入门系列](七)如何使用Go的方法? [Go语言入门系列](八)Go语言是不是面向对象语言? 1. 引入例子 如果你使用 ...

  8. 【Go语言入门系列】Go语言工作目录介绍及命令工具的使用

    [Go语言入门系列]前面的文章: [保姆级教程]手把手教你进行Go语言环境安装及相关VSCode配置 [Go语言入门系列](八)Go语言是不是面向对象语言? [Go语言入门系列](九)写这些就是为了搞 ...

  9. c语言指针与结构体

    #include <stdio.h> #include <stdlib.h> struct mydata { int num; ]; }; void main1() { /*i ...

随机推荐

  1. 你真的清楚DateTime in C#吗?

    DateTime,就是一个世界的大融合.   日期和时间,在我们开发中非常重要.DateTime在C#中,专门用来表达和处理日期和时间. 本文算是多年使用DateTime的一个总结,包括DateTim ...

  2. 大厂程序员教你如何学习C++(内附学习资料)

    目前准备面试同学都知道,C++是百度和腾讯的主流开发语言,而java是阿里的主流开发语言. 对于初学者来说,也不用纠结究竟学习c++还是java 其实只要好好掌握好一门即可,另一门即可融会贯通 因为我 ...

  3. 通过Vue实现的todolist

    和接口对接的todolist因为有后台的存在,todolist获取的数据会一直存在不丢失(不管你如何刷新页面),思路如下: 首先得先搞到接口: 通过这个接口地址可以获取整段的数据,成功err为0. 于 ...

  4. scrapyd+gerapy的项目部署

    scrapyd+gerapy的项目部署: 简单学习,后续跟进完善 声明: 1)仅作为个人学习,如有冒犯,告知速删! 2)不想误导,如有错误,不吝指教! 环境配置: scrapyd下载: pip ins ...

  5. 干货分享:Python Web 部署方式大全

    不要让服务器裸奔 学过PHP的都了解,php的正式环境部署非常简单,改几个文件就OK,用FastCgi方式也是分分钟的事情.相比起来,Python在web应用上的部署就繁杂的多,主要是工具繁多,主流服 ...

  6. Python基础-异常

    异常捕获 常见异常类型 Exception:顶级异常类,大部分异常类都是它的子类.SyntaxError:语法错误TypeError:类型错误ValueError:值错误NameError:找不到名称 ...

  7. maven项目原型(idea)

    1: internal -> appfuse-basic-jsf (创建一个基于Hibernate,Spring和JSF的Web应用程序的原型) 2: internal -> appfus ...

  8. ajax工作原理/实例

    ajax是什么? 是一种创建交互式网页应用的一种网页技术.简单来说,就是向服务器发起请求,获得数据使交互性和用户体验更好. ajax不是一种新的技术,是一些技术的集合体.有 1.XHTML和CSS 2 ...

  9. 微信PC端多开的秘密

    微信电脑端也能多开 昨天,偶然从好朋友小林(微信公众号:小林Coding)处得知,他的电脑居然可以同时上两个微信号. 手机端多开微信我知道,像华为.小米等手机系统都对此做了支持,不过在运行Window ...

  10. 如何用redis做缓存

    redis缓存 在互联网应用中经常需要用redis来缓存热点数据. redis数据在内存,可以保证数据读取的高效,接近每秒数十万次的吞吐量 减少下层持久层数据库读取压力,像mongodb,每秒近千次读 ...