按值传递函数参数,是拷贝参数的实际值到函数的形式参数的方法调用。在这种情况下,参数在函数内变化对参数不会有影响。
成都创新互联公司是专业的山海关网站建设公司,山海关接单;提供网站建设、成都网站建设,网页设计,网站设计,建网站,PHP网站建设等专业做网站服务;采用PHP框架,可快速的进行山海关网站开发网页制作和功能扩展;专业做搜索引擎喜爱的网站,专业的做网站团队,希望更多企业前来合作!
默认情况下,Go编程语言使用调用通过值的方法来传递参数。在一般情况下,这意味着,在函数内码不能改变用来调用所述函数的参数。考虑函数swap()的定义如下。
代码如下:
/* function definition to swap the values */
func swap(int x, int y) int {
var temp int
temp = x /* save the value of x */
x = y /* put y into x */
y = temp /* put temp into y */
return temp;
}
现在,让我们通过使实际值作为在以下示例调用函数swap():
代码如下:
package main
import "fmt"
func main() {
/* local variable definition */
var a int = 100
var b int = 200
fmt.Printf("Before swap, value of a : %d\n", a )
fmt.Printf("Before swap, value of b : %d\n", b )
/* calling a function to swap the values */
swap(a, b)
fmt.Printf("After swap, value of a : %d\n", a )
fmt.Printf("After swap, value of b : %d\n", b )
}
func swap(x, y int) int {
var temp int
temp = x /* save the value of x */
x = y /* put y into x */
y = temp /* put temp into y */
return temp;
}
让我们把上面的代码放在一个C文件,编译并执行它,它会产生以下结果:
Before swap, value of a :100
Before swap, value of b :200
After swap, value of a :100
After swap, value of b :200
这表明,参数值没有被改变,虽然它们已经在函数内部改变。
通过传递函数参数,即是拷贝参数的地址到形式参数的参考方法调用。在函数内部,地址是访问调用中使用的实际参数。这意味着,对参数的更改会影响传递的参数。
要通过引用传递的值,参数的指针被传递给函数就像任何其他的值。所以,相应的,需要声明函数的参数为指针类型如下面的函数swap(),它的交换两个整型变量的值指向它的参数。
代码如下:
/* function definition to swap the values */
func swap(x *int, y *int) {
var temp int
temp = *x /* save the value at address x */
*x = *y /* put y into x */
*y = temp /* put temp into y */
}
现在,让我们调用函数swap()通过引用作为在下面的示例中传递数值:
代码如下:
package main
import "fmt"
func main() {
/* local variable definition */
var a int = 100
var b int= 200
fmt.Printf("Before swap, value of a : %d\n", a )
fmt.Printf("Before swap, value of b : %d\n", b )
/* calling a function to swap the values.
* a indicates pointer to a ie. address of variable a and
* b indicates pointer to b ie. address of variable b.
*/
swap(a, b)
fmt.Printf("After swap, value of a : %d\n", a )
fmt.Printf("After swap, value of b : %d\n", b )
}
func swap(x *int, y *int) {
var temp int
temp = *x /* save the value at address x */
*x = *y /* put y into x */
*y = temp /* put temp into y */
}
让我们把上面的代码放在一个C文件,编译并执行它,它会产生以下结果:
Before swap, value of a :100
Before swap, value of b :200
After swap, value of a :200
After swap, value of b :100
这表明变化的功能以及不同于通过值调用的外部体现的改变不能反映函数之外。
tips: *号,可以指向指针类型内存地址上的值,号,可以获取值类型的内存地址
每一个变量都有内存地址,可以通过变量来操作内存地址中的值,即内存的大小
go语言中获取变量的内存地址方法:通过 符号可以获取变量的地址
定义:普通变量存储的是对应类型的值,这些类型就叫值类型
变量b,在内存中的地址为:0x1040a124,在这个内存地址上存储的值为:156
定义:指针类型的变量存储的是⼀个地址,所以⼜叫指针类型或引⽤类型
b 是值类型,它指向的是内存地址上的值
a是指针类型,它指向的是b的内存地址
指针类型定义,语法: var 变量名 *类型
指针类型在定义完成后,默认为空地址,即空指针(nil)
在定义好指针变量后,可以通过***** 符号可以获取指针变量指向的变量
在这里的 *a 等价于 b,通过修改 *a ,最终修改的是值类型b的值
这里a,d是值类型,b,c是指针类型
d就相当于把a内存地址上值,在内存中从新开辟了一块空间存储,d和a互不影响
b,c相当于指向了a的内存地址,当使用*号引用出内存地址上的变量上,修改值得,a的值也会跟着改变
不一定。
当参数传入到函数中时,函数会在栈空间中,为函数参数开辟对应的内存。
如果传入的是指针,那么系统会为该参数分配指针类型大小的空间,比如32位机就是4个字节。
而传入的变量,分配空间大小就是实际占用的大小。比如char就是1个字节,short就是2个字节,double就是8个字节等。
可以看到,指针和实际变量比较占用空间并没有绝对的大小关系。如果参数是字符型char,那么直接用char比用指针更省内存。
所以具体何种方式更省内存,还需要看参数真正的sizeof值才可以。
go语言中的指针和地址值,在使用上常常具有迷惑性,主要是其特殊的*、符号的使用,可能会让你摸不透,本文希望能讲清楚go语言的指针(pointer)和值(value)。
这里先简单的对指针和地址值概念做一个定义:
这是因为go方法传递参数的方式导致的,go方法函数传递参数传递的是一个拷贝,看看下面的程序会输出什么?
答案是8,而不是9,因为AddAge函数修改的是学生的一个备份,而不是原始的学生对象
如果你想正确的给学生年龄增加的话,函数传递的需要是这个值的指针,如下所示:
需要注意的是,这里我们的指针传递的仍然是一个拷贝,比如,如果你将s赋值给另外一个指针地址,不会影响原有的指针,这点可以自行实践下。
那在使用go语言开发的时候,何时该用指针何时改用地址值呢?比如考虑以下场景:
简单原则: 当你不确定该使用哪种的时候,优先使用指针
如果考虑在数组、切片、map等复合对象中使用指针和值,比如:
很多开发者会认为b会更高效,但是被传递的都是一个切片的拷贝,切片本身就是一个引用,所以这里被传递的其实没有什么区别。
对于指针和地址值的使用,大家需要牢记的一点就是go数据传递的不可变性,活学活用此特点,在无状态函数中此特性非常有用。
因为 interface 类型本质上就是 2 个 uintptr(一个表示 type 一个表示 value)。当你连 2 个 uintptr 都不想拷贝的时候,你就会用到指向 interface 的指针了。
当然,你用一个新的 uintptr 指向另一个 2 个 uintptr 长度的对象,也没省多少事儿
要保持单个对象或者对象很大的时候,传指针
传值的话,传入的是“原对象”的副本,副本任何修改都不会影响“原对象”。
如果不希望改变“原对象”,就传值吧。