Go语言学习教程之指针的示例详解

目录
  • 前言
  • 练习1
  • 练习2
  • 练习3

前言

关于指针的主要几点:

  • 指针类型:一个指针类型*T表示指向给定类型的变量的所有指针的集合,该给定类型T称为基本类型。未初始化的指针的值是nil
  • 变量:一个变量是保存一个值的存储位置。允许的值的集合由变量的类型决定。
  • 寻址操作:对于类型为T的操作数x,寻址操作&x会产生一个指向x的类型为*T的指针。对于指针类型为*T的操作数y,指针间接寻址*y表示y指向的类型为T的变量。

本文使用的Go版本:

$ go version
go version go1.18 darwin/amd64

练习1

var a int = 111
var b *int = &a

fmt.Println("a的值是:", a)   // 111
fmt.Println("a的地址是:", &a) // 0xc000016098
fmt.Println("b的值是:", b)   // 0xc000016098
fmt.Println("b的地址是:", &b) // 0xc0000ac018

*b = *b + 1
fmt.Println(a, b) // 112 0xc000016098

代码中声明了一个整型的变量a,以及一个指向整型变量a*int类型的指针变量b

内存地址表示数据在内存中存放的位置。如上图所示,a相当于是内存地址0xc000016098的一个名字(用于引用计算机内存地址),当我们获取a的值时,就是获取内存地址0xc000016098存储的数据。而指针类型的变量b(代表内存地址0xc0000ac018)存储的是变量a代表的地址,它存储的值就是一个地址。

当使用*b进行指针间接寻址时,可以理解为:找到b代表的内存地址0xc0000ac018中存储的值,存储的是一个地址0xc000016098,于是去拿地址0xc000016098中存储的值111

当对*b进行赋值时(首先赋值符号=右侧已经计算出结果为112了),将b代表的内存地址0xc0000ac018中,存储的地址0xc000016098中,存储的值改为112。修改的是内存地址0xc000016098中存储的值,所以再次打印a(代表内存地址0xc000016098)的值时,已经变为了112

练习2

对于类型为T的操作数x,寻址操作&x会产生一个指向x的类型为*T的指针。

操作数必须是可寻址的,即变量、指针间接引用、切片索引操作;或者一个可寻址的结构体操作数的字段选择;或者一个可寻址的数组的数组索引操作。

有一个特殊的情况是,x可能是一个复合字面量,复合字面量(结构体字面量、数组字面量、切片字面量、映射字面量)是不可寻址的,但是依然可以使用&x。对复合字面量进行&x操作,会生成一个指针,这个指针指向使用字面量的值进行初始化的一个唯一变量。

如果对x的计算会导致运行时错误,那么对&x的计算也会导致运行时错误。

var c float64 = 222.22
fmt.Println(&c) // 1. 对变量c进行寻址操作 0xc0000b2008
var d *float64 = &c
fmt.Println(&*d) // 2.对指针间接引用(*d)进行寻址操作 0xc0000b2008

e := make([]string, 2) // 创建一个初始长度为2的切片
e = []string{"e1", "e2"}
fmt.Println(&e[1]) // 3. 对切片索引操作进行寻址操作 0xc0000b8030

type F struct {
    a string
    b int
}
fmt.Println(&F{"a", 1}) // 4.对结构体字面量进行寻址操作 &{a 1}
var f F = F{"b", 123}
fmt.Println(&f.a) // 5. 对结构体的字段选择进行寻址操作 0xc0000a4048

var g = [3]int{1, 2, 3}       // 创建一个数组
fmt.Println(&g[0])            // 6. 对数组的索引操作进行寻址操作 0xc0000ba000
fmt.Println(&[3]int{4, 5, 6}) // 7. 对数组字面量进行寻址操作 &[4 5 6]

// var h *int = nil
// fmt.Println(*h)  // 会导致一个运行时错误:panic: runtime error: invalid memory address or nil pointer dereference
// fmt.Println(&*h) // 会导致一个运行时错误:panic: runtime error: invalid memory address or nil pointer dereference

练习3

var i int = 1
fmt.Println("i的地址", &i) // i的地址 0xc000016098

increase(i)           // 函数内部i的地址 0xc0000160b0
fmt.Println("i的值", i) // i的值 1

increaseV1(&i)        // 函数内部拿到的i的地址 0xc000016098
fmt.Println("i的值", i) // i的值 2

func increase(i int) {
    fmt.Println("函数内部i的地址", &i)
    i++
}

func increaseV1(ptrI *int) {
    fmt.Println("函数内部拿到的i的地址", &*ptrI)
    *ptrI++
}

将变量作为参数传递到函数中的时候,函数会复制变量中的值到局部变量中,所以不会改变外部变量的值。

在调用increase(i)时,会创建一个新的局部变量i,这个变量i的作用域在函数内部,初始化的值是复制的外部变量i中的值。所以在函数内部执行i++的时候,改变的是局部变量i的值,不会影响到外部变量。执行完之后外部的i的值还是1

当执行increaseV1(&i)时,传入的是一个指向外部i的指针,它表示的地址是外部i的地址0xc000016098,所以在函数内部执行*ptrI++时,改变的是地址0xc000016098中存储的值,执行完函数之后,打印外部的i(代表内存地址0xc000016098)的值,发现值已经变为2了。

到此这篇关于Go语言学习教程之指针的示例详解的文章就介绍到这了,更多相关Go语言指针内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 深入了解Golang的指针用法

    目录 1.指针类型的变量 2.Go只有值传递,没有引用传递 3.for range与指针 4.闭包与指针 5.指针与内存逃逸 与C语言一样,Go语言中同样有指针,通过指针,我们可以只传递变量的内存地址,而不是传递整个变量,这在一定程度上可以节省内存的占用,但凡事有利有弊,Go指针在使用也有一些注意点,稍不留神就会踩坑,下面就让我们一起来细嗦下. 1.指针类型的变量 在Golang中,我们可以通过**取地址符号&**得到变量的地址,而这个新的变量就是一个指针类型的变量,指针变量与普通变量的区别在于

  • Go语言指针用法详解

    结合这个例子分析一下 结果: 结合以往C语言的基础,画了一张图来解释为什么会有上面这些值的出现.先查看下Go中的这两个运算符是啥吧. ①对于所有带a的结果 var a int = 1 定义了一个变量a值为1,如下图所示: &a就是这个存放a变量值的地址 *&a 就是指向&a的一个指针,*&a = a = 1 ②所有带b结果 var b *int = &a 类似C语言的 int *b = &a 定一个指向整形变量的指针b,b指向了a的地址 所以: b = &a

  • Go语言快速入门指针Map使用示例教程

    目录 1. 指针 1.1 指针地址和指针类型 1.2 指针取值 1.3 空指针 1.4 new 的使用 1.5 new与make的区别 2. Map 2.1 什么是Map key,value存储 hash冲突 hash冲突的常见解决方法 开放定址(线性探测)和拉链的优缺点 2.2 Map 定义 2.3 map基本使用 2.4 map的遍历 2.5 map判断某个键是否存在 2.6 map使用delete()函数删除键值对 1. 指针 区别于C/C++中的指针,Go语言中的指针不能进行偏移和运算,

  • Go语言什么时候该使用指针

    目录 什么是指针 指针的声明和定义 var 关键字声明 new 函数声明 指针的操作 指针参数 指针接收者 什么情况下使用指针 什么是指针 我们都知道,程序运行时的数据是存放在内存中的,每一个存储在内存中的数据都有一个编号,这个编号就是内存地址.我们可以根据这个内存地址来找到内存中存储的数据,而内存地址可以被赋值给一个指针.我们也可以简单的理解为指针就是内存地址. 指针的声明和定义 在Go语言中,获取一个指针,直接使用取地址符&就可以.示例: func main() {   name := &qu

  • Go语言学习之指针的用法详解

    目录 引言 一.定义结构体 1. 语法格式 2. 示例 二.访问结构体成员 三.结构体作为函数参数 四.结构体指针 总结 引言 Go 语言中数组可以存储同一类型的数据,但在结构体中我们可以为不同项定义不同的数据类型 结构体是由一系列具有相同类型或不同类型的数据构成的数据集合 结构体表示一项记录,比如保存图书馆的书籍记录,每本书有以下属性: Title :标题 Author : 作者 Subject:学科 ID:书籍ID 一.定义结构体 1. 语法格式 结构体定义需要使用 type 和 struc

  • Go语言学习教程之反射的示例详解

    目录 介绍 反射的规律 1. 从接口值到反射对象的反射 2. 从反射对象到接口值的反射 3. 要修改反射对象,该值一定是可设置的 介绍 reflect包实现运行时反射,允许一个程序操作任何类型的对象.典型的使用是:取静态类型interface{}的值,通过调用TypeOf获取它的动态类型信息,调用ValueOf会返回一个表示运行时数据的一个值.本文通过记录对reflect包的简单使用,来对反射有一定的了解.本文使用的Go版本: $ go version go version go1.18 dar

  • Struts2学习教程之输入校验示例详解

    前言 数据校验几乎是每个应用都要做的工作.用户输入的数据,发送到服务器端,天知道用户输入的数据是否是合法的,是否为恶意输入.所以一个健壮的应用系统必须对用户的输入进行校验,将非法的输入阻止在应用之外,防止这些非法的输入进入系统,从而保证系统的稳定性.安全性. 我们都知道,为了更好的用户体验,以及更高的效率,现在的Web应用都存在以下两重数据校验: 客户端数据校验 服务器端数据校验 对于客户端数据校验主要是通过JavaScript代码来完成:而对于服务器端数据校验是整个应用阻止非法数据的最后防线,

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

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

  • Go语言中的字符串处理方法示例详解

    1 概述 字符串,string,一串固定长度的字符连接起来的字符集合.Go语言的字符串是使用UTF-8编码的.UTF-8是Unicode的实现方式之一. Go语言原生支持字符串.使用双引号("")或反引号(``)定义. 双引号:"", 用于单行字符串. 反引号:``,用于定义多行字符串,内部会原样解析. 示例: // 单行 "心有猛虎,细嗅蔷薇" // 多行 ` 大风歌 大风起兮云飞扬. 威加海内兮归故乡. 安得猛士兮守四方! ` 字符串支持转义

  • R语言学习ggplot2绘制统计图形包全面详解

    目录 一.序 二.ggplot2是什么? 三.ggplot2能画出什么样的图? 四.组装机器 五.设计图纸 六.机器的零件 1. 零件--散点图 1) 变换颜色 2) 拟合曲线 3) 变换大小 4) 修改透明度 5) 分层 6) 改中文 2. 零件--直方图与条形图 1) 直方图 2) 润色 3) 条形图 3. 零件--饼图 4. 零件--箱线图 5. 零件--小提琴图 6. 零件打磨 7. 超级变变变 8. 其他常用零件 七.实践出真知 八.学习资源 九.参考资料 一.序 作为一枚统计专业的学

  • Go语言基础函数基本用法及示例详解

    目录 概述 语法 函数定义 一.函数参数 无参数无返回 有参数有返回 函数值传递 函数引用传递 可变参数列表 无默认参数 函数作为参数 二.返回值 多个返回值 跳过返回值 匿名函数 匿名函数可以赋值给一个变量 为函数类型添加方法 总结 示例 概述 函数是基本的代码块,用于执行一个任务 语法 函数定义 func 函数名称( 参数列表] ) (返回值列表]){ 执行语句 } 一.函数参数 无参数无返回 func add() 有参数有返回 func add(a, b int) int 函数值传递 fu

  • Go语言基础类型及常量用法示例详解

    目录 基础类型 概述 按类别有以下几种数据类型 数值类型 派生类型 变量 概述 单个变量声明 多个变量声明 基础类型 概述 在 Go 编程语言中,数据类型用于声明函数和变量.数据类型的出现时为了把数据分成所需要用大数据的时候才需要申请大内存,这样可以充分的列用内存. 按类别有以下几种数据类型 数值类型 布尔型 bool:布尔型的值只可以是常量 true 或者 false,默认值为 false. 字符串类型 string:编码统一为 UTF-8 编码标识 Unicode 文本,默认值为空字符串.

  • Go语言学习之映射(map)的用法详解

    目录 1. 什么是 map 2. 创建 map 3. 访问 map 4. nil map和空map 5. map中元素的返回值 6. len()和delete() 7. 测试map中元素是否存在 8. 迭代遍历 map 9. 获取map中所有的key 10. 传递map给函数 1. 什么是 map Map 是一种无序的键值对的集合.Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值 Map 是无序的,我们无法决定它的返回顺序,这是因为 Map 是使用 hash

  • Go语言开发框架反射机制及常见函数示例详解

    目录 基本介绍 反射中常见函数和概念 reflect.TypeOf(变量名) reflect.ValueOf(变量名) 变量.interface{}和reflect.Value是可以相互转换的 基本使用 反射注意事项 反射的最佳实践 基本介绍 反射可以在运行时动态获取变量的各种信息,比如变量的类型,类别 如果是结构体变量,还可以获取到结构体本身的信息 通过反射,可以修改变量的值,可以调用关联的方法 使用反射,需要import("reflect") 示意图 反射中常见函数和概念 refl

  • Git基础学习之分支操作的示例详解

    目录 1.新建一个分支并且使分支指向指定的提交对象 2.思考 3.项目分叉历史的形成 4.分支的总结 1.新建一个分支并且使分支指向指定的提交对象 使用命令:git branch branchname commitHash. 我们现在本地库中只有一个 master 分支,并且在 master 分支有三个提交历史. 需求:创建一个 testing 分支,并且testing 分支指向 master 分支第二个版本. # 1.查看提交历史记录 L@DESKTOP-T2AI2SU MINGW64 /j/

随机推荐