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

概述

一图胜千言:

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

值类型与指针类型

1.值类型

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

实例

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、指针变量不能进行运算

实例

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)
 
}

结果:

ptr 指针的值: 0xc000048080
*ptr 指针的值: 100
*ptr 指针的值: 200

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

值与内存地址与指针

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

取地址& 与解引用*

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

运用

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

// 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.值接收者:

func ( variable type )Name ( InputParam ) ( OutputParam )

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

2.指针接受者:

(variable *type)func()

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

指针说明

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

int a=1;
int *p=&a; 

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

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

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)。

int m;
int &n = m;

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

引用的一些规则如下:

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

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

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对其进行引用取其内容的值。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容