go中的值传递和指针传递

news2024/11/13 15:44:07

文章目录

  • 1、& 和 *
  • 2、空指针
  • 3、nil
  • 4、用值传递还是指针传递?
  • 5、补充

1、& 和 *

  • &后跟一个变量名,得到的是这个变量的内存地址
  • *int类型的变量,代表这个变量里存的值是int类型的变量的内存地址
  • 数据类型的指针类型,即在其前面加 *
  • 指针就是内存地址
package main
import(
        "fmt"
)
func main(){

        var age int = 18
        //&符号+变量 就可以获取这个变量内存的地址
        fmt.Println(&age) //0xc0000a2058
        
        //ptr是一个变量,自身也有内存地址
        //&age就是一个地址,是ptr变量的具体的值
        var ptr *int = &age

		//这样直接输出,是ptr这个指针变量的值,即0xc0000a2058
        fmt.Println(ptr)
        
        //ptr这个指针变量自身的地址
        fmt.Println("ptr本身这个存储空间的地址为:",&ptr)
        
        //想获取ptr这个指针或者这个地址指向的那个数据:
        fmt.Printf("ptr指向的数值为:%v",*ptr) //ptr指向的数值为:18
}


在这里插入图片描述

  • 对指针类型的变量再加*,是取真实值,即解引用
x := 10
a := &x // 取变量x的地址,a是一个指向int的指针 (*int 类型)

fmt.Println(*a) // 输出a指向的整数值,即变量x的值,这里将输出 10

2、空指针

* 虽然可以取到指针类型的真实值(解引用),但对nil解引用,会空指针:panic: runtime error: invalid memory address or nil pointer dereference

比如以下情况:

  • 声明了一个指针变量,未初始化就直接解引用
var a *int
fmt.Println(*a)  // 这里将会导致空指针错误
  • 给一个指针变量赋值nil后解引用
var a *int = nil
fmt.Println(*a)  // 这里将会导致空指针错误
  • 调用了一个返回值是指针类型,但返回结果是nil的函数。此时直接解引用会空指针。
func returnNilPointer() *int {
    return nil
}

func main() {
    var a *int = returnNilPointer()
    fmt.Println(*a)  // 这里将会导致空指针错误
}

对指针类型解引用的正确做法是,先判空:

var ptr *int

if ptr != nil {
    fmt.Println(*ptr)  // 安全地解引用ptr
} else {
    fmt.Println("Pointer is nil")
    // 避免解引用nil指针
}

3、nil

源码:
在这里插入图片描述

  • nil是go语言SDK中预先定义好的
  • 可以使用 == 操作符来比较指针、切片、映射、通道和接口变量是否为 nil
  • nil是指针、接口、切片、映射、通道和函数类型的空值
// 指针
var ptr *int
fmt.Println(ptr)  // 输出: nil,即不指向任何有效的内存地址

// 接口
var iface fmt.Stringer
fmt.Println(iface == nil)  // 输出: true,即接口变量不指向任何具体的实现类对象

// 切片
var s []int
fmt.Println(s == nil)  // 输出: true,即表示一个空切片,即长度和容量都为0的切片

// 映射
var m map[string]int
fmt.Println(m == nil)  // 输出: true,表示一个空映射,即不包含任何键值对的映射

// 通道
var ch chan int
fmt.Println(ch == nil)  // 输出: true,即未初始化的通道默认为 nil
// 定义一个函数类型 HandlerFunc
type HandlerFunc func(int) string

// 声明一个 HandlerFunc 类型的变量 handler,但未赋值,其值为nil
 var handler HandlerFunc
  • 因此,在未初始化的通道中发送或者接口数据、在未初始化的map中进行存储或者取值,就会panic,但切片有一点不同
var ch chan int
ch <- 1  // 尝试向空通道发送数据会导致panic

报错:

var m map[string]int
m["key"] = 1  // 尝试在空映射中存储值会导致panic

报错:

  • 只定义,未初始化的切片,其值为nil,表示一个空切片,即长度和容量都为0的切片,此时,直接s[0] = 1就会发生下标越界panic,但用append方法一切正常,append 函数会根据需要自动初始化切片并分配内存
var s []int
s[0] = 1	// 越界panic

在这里插入图片描述

var s []int
s = append(s, 1)	// 不会panic或者空指针
  • 注意,自定义的结构体的空值不是nil,这一点和Java中的null不一样(但如果加了&,即取地址,那就是自定义结构体的指针类型,其空值为nil)

在这里插入图片描述

  • 此外,go中,所有的变量 (包括结构体变量) 在声明时如果没有显式赋值,会被赋予其类型的零值。比如:

在这里插入图片描述

4、用值传递还是指针传递?

什么时候用值传递,什么时候用指针传递?比如向函数调用栈里的下一个方法传递对象A,二者的区别在于,指针传递,传的是对象A的内存地址,传的是一个小巧的地址。值传递,是复制对象A的数据传下去。

之前有个说法:想在调用的函数内部改变对象A的值,就用指针传递,但这句话也不全对,因为用值传递,照样可以实现同样的效果。比如对象A为:

// 矩形
type Rectangle struct {
	Width, Height int
}

此时,要改变矩形的宽,值传递和引用传递(指针传递)的实现如下:

在这里插入图片描述

注意看二者的返回值,值传递,因为不能直接修改原对象,因此,需要将副本对象整个都返回,引用传递,则是一个void方法,因为其接收一个原对象的内存地址,可以直接修改原对象。这两种实现方式,在此时,没有谁优谁劣。

考虑优先使用值传递,原因如下:

  • 对于固定大小的类型(整数、浮点数、小型结构体、小型数组),它们占用的内存大小固定且小,大小与指针大小相当

  • 值传递,代表的意志是:函数收到的是一个副本数据,我只是需要操作这份数据,不会改动你的原始数据

在这里插入图片描述

  • 对于较小的对象,直接值传递,可以避免引用传递时对指针解引用的额外步骤

此外,从底层分析原因:

在这里插入图片描述

最后,如果传递的是一个很大的结构体,那用指针传递更优。

5、补充

关于使用指针类型的场景,还有:insert数据时,数据库中默认值不对的时候:

在这里插入图片描述

=====

最后一篇博客,部门被裁,中午通知,下午走人。纪念人生第一次失业哈哈哈哈哈哈哈哈。

20248212:27

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

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

相关文章

顺序表的实现【数据结构】

1.线性表 线性表&#xff08;linear list&#xff09;是n个具有相同特性的数据元素的有线序列。线性表是一种在实际中广泛使用的数据结构&#xff0c;常见的线性表有&#xff1a;顺序表、链表、栈、队列、字符串… 线性表在逻辑上是线性结构&#xff0c;也就是说是连续的一条线…

医院设置(洛谷)

设有一棵二叉树&#xff0c;如图&#xff1a; 其中&#xff0c;圈中的数字表示结点中居民的人口。圈边上数字表示结点编号&#xff0c;现在要求在某个结点上建立一个医院&#xff0c;使所有居民所走的路程之和为最小&#xff0c;同时约定&#xff0c;相邻接点之间的距离为 11。…

C语言实现 -- 单链表

C语言实现 -- 单链表 1.顺序表经典算法1.1 移除元素1.2 合并两个有序数组 2.顺序表的问题及思考3.链表3.1 链表的概念及结构3.2 单链表的实现 4.链表的分类 讲链表之前&#xff0c;我们先看两个顺序表经典算法。 1.顺序表经典算法 1.1 移除元素 经典算法OJ题1&#xff1a;移除…

在服务器上使用Dockerfile创建springboot项目的镜像和踩坑避雷

1. 准备个文件夹 这是我的路径 /usr/local/springboot/docker-daka/docker_files2. 将jar包上传 springboot项目打包——maven的package 这是整个项目打包的模式&#xff0c;也可以分离依赖、配置和程序进行打包&#xff0c;详情看我这篇文章&#xff1a; springboot依赖 配…

java基础 之 集合与栈的使用(四)

文章目录 Queue栈Stack队列和栈的区别小扩展自己写个简单的队列自己写个简单的栈使用栈来实现个队列使用队列来实现个栈写在最后 前文回顾&#xff1a; 戳这里 → java基础 之 集合与栈的使用&#xff08;一&#xff09; 戳这里 → java基础 之 集合与栈的使用&#xff08;二&a…

windows中node版本的切换(nvm管理工具),解决项目兼容问题 node版本管理、国内npm源镜像切换(保姆级教程,值得收藏)

前言 在工作中&#xff0c;我们可能同时在进行2个或者多个不同的项目开发&#xff0c;每个项目的需求不同&#xff0c;进而不同项目必须依赖不同版本的NodeJS运行环境&#xff0c;这种情况下&#xff0c;对于维护多个版本的node将会是一件非常麻烦的事情&#xff0c;nvm就是为…

【Git】git 从入门到实战系列(二)—— git 介绍以及安装方法 (文末附带视频录制操作步骤)

文章目录 一、前言二、git 是什么三、版本控制系统是什么四、本地 vs 集中式 vs 分布式本地版本控制系统集中式版本控制系统分布式版本控制系统 五、安装 git 一、前言 本系列上一篇文章【Git】git 从入门到实战系列&#xff08;一&#xff09;—— Git 的诞生&#xff0c;Lin…

Linux系统编程 --- 基础IO

形成共识原理&#xff1a; 1、文件 内容 属性 2、文件分为打开的文件和没打开的文件 3、打开的文件&#xff1a;谁打开&#xff1f;进程&#xff01;--- 本质是研究进程和文件的关系&#xff01; 文件被打开&#xff0c;必须先被加载到内存&#xff01; 一个进程可以打开…

PyTorch 训练自定义功能齐全的神经网络模型的详细教程

在前面的文章中&#xff0c;老牛同学介绍了不少大语言模型的部署、推理和微调&#xff0c;也通过大模型演示了我们的日常的工作需求场景。我们通过大语言模型&#xff0c;实实在在的感受到了它强大的功能&#xff0c;同时也从中受益颇多。 今天&#xff0c;老牛同学想和大家一…

【Android Studiio】default activity 原生安卓和uniapp默认启动分析

文章目录 思路&#xff1a; 一、原生安卓二、uniapp 探究方向&#xff1a;找到Default Activity 思路&#xff1a; 在Android开发中&#xff0c;"default activity"这个概念通常指的是应用启动时默认会加载和显示的那个Activity。AndroidManifest.xml文件是Android…

基于Selenium实现操作网页及操作windows桌面应用

Selenium操作Web页面 Why? 通常情况下&#xff0c;网络安全相关领域&#xff0c;更多是偏重于协议和通信。但是&#xff0c;如果协议通信过程被加密或者无法了解其协议构成&#xff0c;是无法直接通过协议进行处理。此时&#xff0c;可以考虑模拟UI操作&#xff0c;进而实现相…

声音和数据之间的调制解调 —— 电报机和电传打字机如何影响计算机的演变

注&#xff1a;机翻&#xff0c;未校对。 The Squeal of Data The through line between the telegraph and the computer is more direct than you might realize. Its influence can be seen in common technologies, like the modem. 电报和计算机之间的直通线比你想象的要…

基于IOT架构的数据采集监控平台!

LP-SCADA数据采集监控平台是蓝鹏测控推出的一款聚焦于工业领域的自动化数据采集监控系统&#xff0c; 助力数字工厂建设的统一监控平台。 为企业提供从下到上的完整的生产信息采集与集成服务&#xff0c;从而为企业综合自动化、工厂数字化及完整的"管控一体化”的解决方案…

LockSupport详解

文章目录 理解可重入锁LockSupport线程等待唤醒机制&#xff08;wait/notify&#xff09; waitNotify限制awaitSignal限制LockSupport重点说明 理解可重入锁 可重入锁的种类&#xff1a; 隐式锁&#xff08;即synchronized关键字使用的锁&#xff09;默认是可重入锁。 同步代…

站在临床数据科学的角度,药物试验归根结底是这两大假设

在临床数据科学的领域中&#xff0c;药物试验的设计和实施是评估药物效果及其安全性的关键环节。药物试验的基础无外乎两大核心假设&#xff1a;有效性与安全性。这两个假设不仅是药物试验的起点&#xff0c;也是整个研究过程中的重要指导原则。 药物试验的核心主旨在于对待测试…

Python高性能计算:进程、线程、协程、并发、并行、同步、异步

这里写目录标题 进程、线程、协程并发、并行同步、异步I/O密集型任务、CPU密集型任务 进程、线程、协程 进程、线程和协程是计算机程序执行的三种不同方式&#xff0c;它们在资源管理、执行模型和调度机制上有显著的区别。以下是对它们的详细解释和比较&#xff1a; 进程&…

一款有趣的工具,锁定鼠标键盘,绿色免安装

这是一款完全免费的程序&#xff0c;可以实现在不锁定屏幕的情况下锁定鼠标键盘&#xff0c;让鼠标键盘无法操作。比较适合防止误碰鼠标键盘&#xff0c;以及离开电脑时不希望别人操作自己的电脑。 ★★★★★锁定鼠标键盘工具&#xff1a;https://pan.quark.cn/s/e5c518a2165…

路由配置修改(五)

一、默认约定式路由 1、umi 会根据 pages 目录自动生成路由配置。 * name umi 的路由配置* description 只支持 path,component,routes,redirect,wrappers,name,icon 的配置* param path path 只支持两种占位符配置&#xff0c;第一种是动态参数 :id 的形式&#xff0c;第二种…

win11 intel新显卡控制面板无自定义分辨率选项解决

问题 下图是现在的intel显卡控制面板&#xff0c;不知道为啥变得很傻瓜式了&#xff0c;连所有显卡控制面板都有的分辨率自定义也被干掉了。 解决方式 其实解决很简单&#xff0c;因为自定义分辨率对显卡玩游戏来说还是很常用的&#xff0c;intel在beta版又加回来了&#x…

样式与特效(2)——新闻列表

1.盒子模型的边距概念 ) Margin-top 上面 Margin-bottom 底部 Margin-right 右边 Margin-left 左边 Margin : 10px &#xff08;上下左右都是10px&#xff09; Margin &#xff1a;10px,20px (上下边距10px 左右20px) CSS里面最重要的属性之一 将页面理解成…