Go语言基本语法(三)指针

news2025/1/12 1:57:52

什么是指针

在Go语言中,"指针是一种存储变量内存地址的数据类型",意味着指针本身是一个特殊的变量,它的值不是数据本身,而是另一个变量在计算机内存中的位置(地址)。形象地说,就像存放了一个数据项在内存中的“门牌号”。

"它允许程序直接操纵内存中的数据",这句话意味着通过指针,程序能够绕过变量本身,直接到达变量在内存中的存储位置,并对那里的数据进行读取或修改。这种能力非常重要,因为:

1. **效率**:当数据结构很大时(如大型数组或结构体),直接操作其内存地址可以避免复制整个数据结构,从而节省时间和空间。
2. **灵活性**:在函数调用时,传递数据的指针而非数据本身的副本,可以使函数有能力修改调用者的数据,这在很多场景下是必要的,比如更新共享状态或配置。
3. **控制权**:指针提供了底层的内存访问能力,这对于系统编程、性能优化和一些高级数据结构的实现至关重要。

例如,如果你有一个很大的数组,想要修改其中的一个元素,直接通过指针定位到那个元素的内存位置并修改它,比起先复制整个数组或结构到函数内部再修改,显然更高效。此外,通过指针,你还可以创建动态数据结构,如链表、树等,因为每个节点可以指向下一个节点的位置。

相比之下,C/C++的指针以其高度灵活性闻名,允许自由的偏移和运算,这为系统级编程和大数据操作提供了强大工具,也是其高性能的来源。然而,这种灵活性也带来了风险,如内存泄漏、指针悬挂、缓冲区溢出等问题,这些安全漏洞常常成为黑客攻击的入口,也是操作系统频繁更新修复的原因之一。

指针的概念

指针地址和指针类型

在Go语言中,理解和操作指针时,"指针地址"和"指针类型"是两个核心概念:

指针地址

指针地址指的是一个变量在内存中的实际存储位置。在Go语言中,你可以使用`&`操作符来获取一个变量的地址。这个地址是一个无符号整数,但它通常以十六进制形式显示,代表了变量在内存中的确切位置。例如:

var num int = 10
var ptr *int = &num

在这个例子中,`&num`就是获取变量`num`的内存地址,并将其赋值给指针变量`ptr`,`ptr`的类型就是指向`int`类型的指针,即`*int`。

指针类型

指针类型定义了指针所指向的数据类型。每个指针都有一个明确的类型,它决定了该指针可以指向哪种类型的变量。在Go语言中,指针类型的声明语法是在数据类型前加上星号`*`。例如,`*int`表示一个指向整型变量的指针,`*string`表示一个指向字符串变量的指针。

指针类型的重要性在于,它确保了类型安全,意味着你不能错误地将一个类型的指针赋值给另一个不匹配类型的指针变量,除非通过类型断言或类型转换(在类型兼容的情况下)。

指针的使用

package main
import (
    "fmt"
)
func main() {
    var money int = 156
    var str string = "ppp"
    fmt.Printf("%p %p", &money, &str)
}

运行结果:

指针取值

当使用`&`操作符对普通变量进行取地址操作并得到变量的指针后,可以对指针使用`*`操作符,也就是指针取值,代码如下:

package main
import (
    "fmt"
)
func main() {
    // 准备一个字符串类型
    var day01 = "Hello World"
    // 对字符串取地址, tem类型为*string
    tem := &day01
    // 打印tem的类型
    fmt.Printf("tem type: %T\n", tem)
    // 打印ptr的指针地址
    fmt.Printf("address: %p\n", tem)
    // 对指针进行取值操作
    value := *tem
    // 取值后的类型
    fmt.Printf("value type: %T\n", value)
    // 指针取值后就是指向变量的值
    fmt.Printf("value: %s\n", value)
}

运行结果:


取地址操作符&和取值操作符*是一对互补操作符,&取出地址,*根据地址取出地址指向的值。
变量、指针地址、指针变量、取地址、取值的相互关系和特性如下:

  • 对变量进行取地址操作使用&操作符,可以获得这个变量的指针变量。
  • 指针变量的值是指针地址。
  • 对指针变量进行取值操作使用*操作符,可以获得指针变量指向的原变量的值。

使用指针修改值

通过指针不仅可以取值,也可以修改值:

示例1:

package main

import "fmt"

// 交换函数
func swap(a, b *int) {

    // 取a指针的值, 赋给临时变量t
    t := *a

    // 取b指针的值, 赋给a指针指向的变量,
    *a = *b

    // 将a指针的值赋给b指针指向的变量
    *b = t
}

func main() {

// 准备两个变量, 赋值1和2
    x, y := 1, 2

    // 交换变量值
    swap(&x, &y)

    // 输出变量值
    fmt.Println(x, y)
}

运行结果:

*操作符作为右值时,意义是取指针的值,作为左值时,也就是放在赋值操作符的左边时,表示 a 指针指向的变量。其实归纳起来,*操作符的根本意义就是操作指针指向的变量。当操作在右值时,就是取指向变量的值,当操作在左值时,就是将值设置给指向的变量。

如果在 swap() 函数中交换操作的是指针值,会发生什么情况?可以参考下面代码:

package main

import "fmt"

func swap(a, b *int) {
    b, a = a, b
}

func main() {
    x, y := 1, 2
    swap(&x, &y)
    fmt.Println(x, y)
}

运行结果:

结果表明,交换是不成功的。上面代码中的 swap() 函数交换的是 a 和 b 的地址,在交换完毕后,a 和 b 的变量值确实被交换。但和 a、b 关联的两个变量并没有实际关联。这就像写有两座房子的卡片放在桌上一字摊开,交换两座房子的卡片后并不会对两座房子有任何影响。

示例2:

package main

import "fmt"

func main() {
	// 声明一个整型变量
	var num int = 10

	// 声明一个指向整型的指针
	var ptr = &num

	//将50赋给ptr指针指向的变量
	*ptr = 50

	fmt.Println(num) //输出50
	fmt.Println(*ptr) //输出50
}

指针的指针

指针的指针,顾名思义,就是一个指针变量,它的值是另一个指针的地址。在Go语言中,正如你可以声明指向任何基本类型或复合类型的指针一样,你也可以声明指向指针的指针。这种多级指针可以用来表示更加复杂的内存关系,或者在某些情况下,为了通过函数传递指针并修改指针本身(而不仅仅是指针指向的值)时使用。

声明与初始化

假设你有一个整型指针*int,那么一个指向这个整型指针的指针就会是**int。声明和初始化一个指针的指针的方式如下:

package main
import "fmt"

func main() {
    // 声明一个整型变量
    var num int = 10
    
    // 声明一个指向整型的指针
    var ptr *int = &num
    
    // 声明一个指向指针的指针(即指针的指针)
    var ptrToPtr **int = &ptr
    
    // 修改通过指针的指针访问的值
    **ptrToPtr = 20
    
    fmt.Println(num)       // 输出: 20
    fmt.Println(*ptr)      // 输出: 20
    fmt.Println(*ptrToPtr) // 输出: 地址,显示ptr的地址
}

使用场景

指针的指针在实际编程中的使用相对较少,但在某些特定场景下非常有用,例如:

  • 当你需要通过函数修改一个指针变量本身(比如让指针指向不同的内存地址)时。
  • 在配置或设置结构体的指针成员时,特别是这些成员也是指针类型。
  • 在某些高级的数据结构或底层系统编程中,用于复杂的数据操作和内存管理。

new() 函数

Go语言还提供了另外一种方法来创建指针变量,格式如下:

new(类型)

一般这样写:

str := new(string)
*str = "Go语言教程"
fmt.Println(*str)

new() 函数可以创建一个对应类型的指针,创建过程会分配内存,被创建的指针指向默认值。

参考文章:(6 封私信 / 42 条消息) Go 语言怎么定义和使用指针? - 知乎 (zhihu.com)

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

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

相关文章

Avalonia .NET构建Linux桌面应用

目录 👻前言 💻安装Avalonia 📦创建项目 📚在win下运行 ​🔑打包发布​编辑 📻在linux下运行 环境WIN10 VS2022 debian 👻前言 Avalonia 是一个用于创建跨平台用户界面 (UI) 的开源框架…

C++——STL容器——vector

vector是STL容器的一种,和我们在数据结构中所学的顺序表结构相似,其使用和属性可以仿照顺序表的形式。vector的本质是封装了一个动态大小的数组,支持动态管理容量、数据的顺序存储以及随机访问。 1.前言说明 vector作为容器,应该…

对6个默认成员函数的总结

前言:本篇文章是对六大默认成员函数的自我总结,不适合刚入门的新人学习。适合想进一步深入了解六大默认成员函数的人学习。 1.构造函数:给对象初始化的函数,相当于之前写的Init函数。 构造函数的特性: 对内置类型不…

深度解析:人工智能作画算法的原理与技术

引言 在数字艺术的探索中,人工智能(AI)作画算法以其独特的创造性和艺术性引起了广泛的兴趣。这些算法不仅仅是简单的图像处理工具,它们背后蕴藏着复杂的神经网络和深度学习模型。本文将深入探讨AI作画算法的原理与技术&#xff0…

day15 学一下Tailwindcss(java转ts全栈/3r教室)

目前距离全栈差得最多的是前端,而对于前端主要是CSS一直不熟悉,觉得很复杂写起来总是不上道,所以特别关注下Tailwindcss吧,其他前端框架可以先放放,多说无益直接用tailwindcss做个页面试试 看下文档:Tailwi…

【LeetCode刷题记录】104. 二叉树的最大深度

104 二叉树的最大深度 给定一个二叉树 root ,返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 示例 1: 输入:root [3,9,20,null,null,15,7] 输出:3 示例 2: 输入&#xff…

HarmonyOS开发案例:【排行榜页面】

介绍 本课程使用声明式语法和组件化基础知识,搭建一个可刷新的排行榜页面。在排行榜页面中,使用循环渲染控制语法来实现列表数据渲染,使用Builder创建排行列表布局内容,使用装饰器State、Prop、Link来管理组件状态。最后我们点击…

基于python+django网易新闻+评论的舆情热点分析平台

博主介绍: 大家好,本人精通Java、Python、C#、C、C编程语言,同时也熟练掌握微信小程序、Php和Android等技术,能够为大家提供全方位的技术支持和交流。 我有丰富的成品Java、Python、C#毕设项目经验,能够为学生提供各类…

回溯Backtracking Algorithm

目录 1) 入门例子 2) 全排列-Leetcode 46 3) 全排列II-Leetcode 47 4) 组合-Leetcode 77 5) 组合总和-Leetcode 39 6) 组合总和 II-Leetcode 40 7) 组合总和 III-Leetcode 216 8) N 皇后 Leetcode 51 9) 解数独-Leetcode37 10) 黄金矿工-Leetcode1219 其它题目 1) 入…

LeetCode45:跳跃游戏Ⅱ

题目描述 给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。 每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说&#xff0c;如果你在 nums[i] 处&#xff0c;你可以跳转到任意 nums[i j] 处: 0 < j < nums[i] i j < n 返回到达 nums[n …

SAP PP学习笔记08 - 作业区(工作中心Work Center),作业区Customize

上一章讲了作业手顺&#xff08;工艺路线Routing&#xff09;。 SAP PP学习笔记07 - 作业手顺&#xff08;工艺路线Routing&#xff09;-CSDN博客 这一章来讲讲作业区&#xff08;工作中心 Work Center&#xff09;。 1&#xff0c;作业区&#xff08;工作中心&#xff09;中…

Linux挂载硬盘

1、查看硬盘数量 fdisk -l # 可以看到三个磁盘 # /dev/vda 50G # /dev/vdb 100G 新增 # /dev/vdc 100G 新增2、查看当前挂载情况 df -h # 可以看到50G的已经挂载3、格式化待挂载盘 # 对新的数据盘进行挂载前要进行格式化&#xff0c;只有格式化后才可以挂载 mkfs.ext4 /dev/…

2024年这样做抖音小店,操作简单,起店稳定!

大家好&#xff0c;我是电商糖果 不少朋友说跟糖果抱怨过&#xff0c;说抖音小店越来越难做了。 平台的规则越来越多&#xff0c;商家运营店铺的时候&#xff0c;很容易出现违规预警。 糖果是2020年开始做的抖音小店&#xff0c;现在已经经营了多家小店。 实话实说确实比之…

一站式AI创作平台:融合GPT会话、GPTs应用、Midjourney视觉艺术与Suno AI音乐合成模块

一、系统简介 星河易创AI系统基于ChatGPT的核心技术打造&#xff0c;集成了自然语言问答和艺术创作功能。该系统兼容Midjourney绘画技术&#xff0c;并支持官方GPT模型。它提供了多样化的应用&#xff0c;包括GPTs的多场景应用、实时GPT语音对话能力、GPT-4模型的先进特性&…

扩展大型视觉-语言模型的视觉词汇:Vary 方法

在人工智能领域&#xff0c;大型视觉-语言模型&#xff08;LVLMs&#xff09;正变得越来越重要&#xff0c;它们能够处理多种视觉和语言任务&#xff0c;如视觉问答&#xff08;VQA&#xff09;、图像字幕生成和光学字符识别&#xff08;OCR&#xff09;。然而&#xff0c;现有…

springboot 集成 flowable

随着企业对于业务流程管理需求的增加&#xff0c;流程引擎在企业信息化建设中的作用越来越重要。Flowable是一个开源的轻量级业务流程管理&#xff08;BPM&#xff09;和工作流引擎&#xff0c;它支持BPMN 2.0标准。 Flowable的一些特点&#xff1a; 安装集成&#xff1a;Flow…

基于Springboot 的 Excel表格的导入导出

首先 &#xff0c;引入相关依赖EasyPOI <dependency><groupId>cn.afterturn</groupId><artifactId>easypoi-spring-boot-starter</artifactId><version>4.4.0</version></dependency> 编写实体类&#xff1a; Data AllArgs…

Golang错误处理机制

文章目录 Golang错误处理机制panic异常recover捕获异常自定义错误 Golang错误处理机制 panic异常 panic异常 Go的类型系统会在编译时捕获很多错误&#xff0c;但有些错误只能在运行时检查&#xff0c;比如除零错误、数组访问越界、空指针引用等&#xff0c;这些运行时错误会引…

mongodb卸载(win)

关闭服务 &#xff08;或者cmd卸载服务&#xff1a;&#xff09; net stop 服务名称卸载应用 至此&#xff0c;卸载完成&#xff01;

手拉手CentOS 安装 mysql-5.7

MySQL是一种关系型数据库管理系统&#xff0c;关系数据库将数据保存在不同的表中&#xff0c;而不是将所有数据放在一个大仓库内&#xff0c;这样就增加了速度并提高了灵活性。 tar.gz包安装 #如没有安装wget则无法使用&#xff0c;以装&#xff0c;则直接省略该步~&#xff…