前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Go 指针与引用:值传递和址传递

Go 指针与引用:值传递和址传递

作者头像
一个会写诗的程序员
发布2022-05-13 15:07:19
1.7K0
发布2022-05-13 15:07:19
举报
文章被收录于专栏:一个会写诗的程序员的博客

概述

一图胜千言:

说明: 1、变量是抽象出来的概念,变量即表示内存值(在程序运行时). 2、指针即内存地址, 内存值所在的内存空间的编号. 3、指针变量:引用计算机的内存地址.

值类型与指针类型

1.值类型

定义:变量直接指向存在内存中的值,我们称之为值类型。 值类型的变量的值存储在栈中。 值类型,将一个变量赋值给另一个变量,称为值拷贝。

实例

代码语言:javascript
复制
package main
 
import "fmt"
 
func main(){
    var num1,num2 int
    num1 = 10
    num2 = num1 // 值类型赋值操作
 
    fmt.Println(num1,num2) // 10 10
 
    num1 = 20
    fmt.Println(num1,num2) // 20 10
}

2.指针类型

定义:一个变量指向内存中值所在的内存地址,我们称这个变量为指针类型

go 语言中的指针与C/C++ 中的指针用法是一样的,只是出于安全性的考虑go增加了: 1、不同类型的指针不能互相转化 2、任何普通指针类型*T和uintptr之间不能互相转化 3、指针变量不能进行运算

实例

代码语言:javascript
复制
package main
 
import "fmt"
 
func main(){
    var num int = 100
    var ptr *int  // 类型前 加 * 表示这是指针类型,指针类型的初始值为nil ,和其他语言的NUll,None一样
        ptr = &num // & 取num 变量的内存地址。 因为ptr 是指针,指向的是内存地址,所以需要赋值操作的是内存地址
        fmt.Println("ptr 指针的值:",ptr)
        fmt.Println("*ptr 指针的值:",*ptr) // * 取指针内存地址所指向的值
 
        num = 200
        fmt.Println("*ptr 指针的值:",*ptr)
 
}

结果:

代码语言:javascript
复制
ptr 指针的值: 0xc000048080
*ptr 指针的值: 100
*ptr 指针的值: 200

*ptr 没有操作,为什么值发生了变化 ptr 是指针类型,并被赋予了 num 的内存地址,当num值发现变化时,实际也就是 ptr 内存地址所对应的值变了 因为 ptr内存地址所对应的 内存值就是num的值

值与内存地址与指针

1、变量是抽象出来的概念,go语言程序运行时即表示内存值 2、内存地址即 内存值所在的内存空间的编号 3、指针变量:是一种占位符,用于引用计算机的内存地址。可理解为内存地址的标签

取地址& 与解引用*

说明 在go语言中我们可以通过 & 是取地址符号 , 即取得某个变量的地址 , 如 ; &a * 是指针运算符 , 可以表示一个变量是指针类型 , 也可以表示一个指针变量所指向的存储单元 , 也就是这个地址所存储的值。

运用

代码语言:javascript
复制
package main
 
import "fmt"
 
func main(){
    var n1,n2,n3 int
    n1 = 100
    n2 = 200
    n3 = 3
    swap(&n1,&n2) // 传内存地址
    add(&n3) // 取内存地址
    fmt.Println(n1)  // 200
    fmt.Println(n2) // 100
    fmt.Println(n3) // 4
 
 
    var num *int  // 申明一个int 指针类型
    num = &n3  // 所以可以赋值 内存地址
    add(num)
    fmt.Println("num指针变量的内存值:",*num) // *num == 5 
    // 此时n3 == 5
}
 
 
// 传入 两个指针类型的数据.
func swap(i,j *int){
    *i,*j = *j,*i // 值的替换
}
 
func add(num *int){
    // *num 解引用
    *num = *num + 1
}

引用类型

在go语言中目前引用类型有: 切片、map、chan、func。 空指针(引用): nil

代码语言:javascript
复制
// nil is a predeclared identifier representing 
// the zero value for a pointer, channel, func, interface, map, or slice type.
var nil Type // Type must be a pointer, channel, func, interface, map, or slice type

引用类型可以简单的理解为指针。

值接受者与指针接受者

1.值接收者:

代码语言:javascript
复制
func ( variable type )Name ( InputParam ) ( OutputParam )

值接受者可以给接收值、也可以接收指针,因为go会对接收的指针进行解引用。但是!variable传递进函数的只是副本,他们都是在variable的副本上进行操作,并不影响 variable 的原本的值。

2.指针接受者:

代码语言:javascript
复制
(variable *type)func()

指针接收者接收的是variable的值的地址,也就是说func修改了值的时候会影响 variable 原本的值。

指针说明

(1) 指针:指针是一个变量,只不过这个变量存储的是一个地址,指向内存的一个“存储单元”,即指针是一个实体;而引用跟原来的变量实质上是同一个东西,只不过是原变量的一个别名而已。如:

代码语言:javascript
复制
int a=1;
int *p=&a; 

上面2 行代码,定义了一个整形变量和一个指针变量p,该指针变量指向a的存储单元,即p的值是a存储单元的地址。

而下面2行代码,定义了一个整形变量a=1, 和这个整型a的引用b : &b=a。 事实上a和b是同一个东西,在内存占有同一个存储单元。

代码语言:javascript
复制
int a=1;
int &b=a;

(2) 可以有const指针,但是没有const引用

(3) 指针可以有多级,但是引用只能是一级(int **p;合法 而 int &&a是不合法的)

(4) 指针的值可以为空,但是引用的值不能为NULL,并且引用在定义的时候必须初始化;

(5) 指针的值在初始化后可以改变,即指向其它的存储单元,而引用在进行初始化后就不会再改变了,从一而终

(6)”sizeof引用”得到的是所指向的变量(对象)的大小,而”sizeof指针”得到的是指针本身的大小;

(7)指针和引用的自增(++)运算意义不一样;

指针与引用的相同点

都是地址的概念;

指针指向一块内存,它的内容是所指内存的地址;

引用是某块内存的别名。

联系

1、引用在语言内部用指针实现(如何实现?)

2、对一般应用而言,把引用理解为指针,不会犯严重语义错误。引用是操作受限了的指针(仅容许取内容操作)。

引用是C++中的概念,初学者容易把引用和指针混淆一起。以下程序中,n是m的一个引用(reference),m 是被引用物(referent)。

代码语言:javascript
复制
int m;
int &n = m;

n 相当于m 的别名(绰号),对n 的任何操作就是对m 的操作

引用的一些规则如下:

代码语言:javascript
复制
(1)引用被创建的同时必须被初始化(指针则可以在任何时候被初始化)。(2)不能有NULL 引用,引用必须与合法的存储单元关联(指针则可以是NULL)。(3)一旦引用被初始化,就不能改变引用的关系(指针则可以随时改变所指的对象)。

以下示例程序中,k 被初始化为i 的引用。语句k = j 是把k 的值改变成为6,由于k 是i 的引用,所以i 的值也变成了6.

代码语言:javascript
复制
int i = 5;
int j = 6;
int &k = i;
k = j; // k 和i 的值都变成了6

上面的程序看起来象在玩文字游戏,没有体现出引用的价值。引用的主要功能是传递函数的参数和返回值

C++语言中,函数的参数和返回值的传递方式有三种:值传递、指针传递和引用传递。 “引用传递”的性质像“指针传递”,而书写方式像“值传递”。实际上“引用”可以做的任何事情“指针”也都能够做,为什么还要“引用”这东西? 答案是“用适当的工具做恰如其分的工作”。

指针能够毫无约束地操作内存中的如何东西,尽管指针功能强大,但是非常危险

指针,就象一把刀,它可以用来砍树、裁纸、修指甲、理发等等,谁敢这样用?

如果的确只需要借用一下某个对象的“别名”,那么就用“引用”,而不要用“指针”,以免发生意外。比如说,某人需要一份证明,本来在文件上盖上公章的印子就行了,如果把取公章的钥匙交给他,那么他就获得了不该有的权利。

总的来说,在以下情况下你应该使用指针:

一是你考虑到存在不指向任何对象的可能(在这种情况下,你能够设置指针为空),

二是你需要能够在不同的时刻指向不同的对象(在这种情况下,你能改变指针的指向)。如果总是指向一个对象并且一旦指向一个对象后就不会改变指向,那么你应该使用引用。

还有一种情况,就是当你重载某个操作符时,你应该使用引用。

尽可能使用引用,不得已时使用指针。

当你不需要“重新指向”时,引用一般优先于指针被选用。这通常意味着引用用于类的公有接口时更有用。引用出现的典型场合是对象的表面,而指针用于对象内部。

指针引用和值引用区别

区分指针引用和值引用,使用struct的时候,明确指针引用和值引用的区别很重要。

1.值引用赋值 比如 a:=b,这样修改a.name=“ls”,不会影响到b.name,值引用是复制结构体,开辟一块新的内存空间, a只是b的一个副本,而不是指向b的引用。

2.指针引用赋值 比如 a:=&b ,这样修改a.name=“ls”,会影响到b.name,指针引用是指向结构体内存地址的引用,同一块内存空间 总结1:值引用,两个变量值是独立的,而指针引用则会互相影响,因为他们都指向同一块内存地址。

总结2:值引用只是复制的一个副本,不是指向内存地址的引用;指针引用,指针是指向内存地址的引用,因此使用它操作的不是结构体的副本而是本身。

总结3:指针引用的时候,比如 b:=&a, 此时b是指针,因此必须使用*b对其进行引用取其内容的值。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-05-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概述
  • 值类型与指针类型
    • 1.值类型
      • 2.指针类型
        • 值与内存地址与指针
          • 取地址& 与解引用*
            • 引用类型
            • 值接受者与指针接受者
              • 1.值接收者:
                • 2.指针接受者:
                • 指针说明
                • 指针与引用的相同点
                • 联系
                • 指针引用和值引用区别
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档