版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
1.值传递与引用传递形象化理解:
引用传值:相当于把烧饼串成一串,然后是有底座的,这个底座就相当于地址,这个地址对应的烧饼就相当于变量,这个地址对应的所有的变量都是一样的;
值传递:各个烧饼都是平放的,新的烧饼只是复制了之前的烧饼,之后被吃了,或者被煮了,与最开始的烧饼无关了
传地址进函数,函数内外的变量一起变化
传值进去,函数内的变化不影响函数外
2.分析两者:
2.1采用引用传值的时候:
函数内部用&:
我们希望这个地址对应的变量会随着变化 所以使用& 一般是函数内部,因为函数内部就是要变量变化的嘛。
函数内对指定变量进行赋值(这里的赋值其实是地址对应的变量的赋值,所以用&)
与return返回值的时候使用&(这里返回也是返回&{变量}))
函数声明的时候*:
因为是最终的结果 所以直接用* 不用再在乎是否变化 一般是函数的入参与返回参数
2.2采用值传递的时候:
赋值用值传递 返回值的时候也用值传递 过程中也用值传递就可以 都没有符号
3.属性加*的理解
一般属性
因为int对应iptr,因为int是一个地址,所以iptr也是一地址;
我们传入的参数就是以地址&i;
函数里面:*iptr=*&i=0
我们对地址(iptr &i)进行赋值的,所以变量(*iptr i)都会发生变化;
这是从根上地址发生的变化;
所以我们对属性加*的目的就很清楚了,
让函数内外的变量一起变化。
属性加*属性的话 调用的地方必定是&变量
集合属性结构体
结构体加&与不加&进行赋值修改操作没有任何影响。
package main
import "fmt"
type person struct {
name string
age int
}
var a int
func main() {
// 一般展示
// 结构体加&赋值 &{Ann 40}
// 结构体不加赋值 {Ann 40}
fmt.Println("结构体加&赋值",&person{name: "Ann", age: 40})
fmt.Println("结构体不加赋值",person{name: "Ann", age: 40})
// 加与不加获取属性值
// 不加&进行获取内部属性值 50
// 加&进行获取内部属性值 50
a := person{name: "Sean", age: 50}
b := &person{name: "Sean", age: 50}
fmt.Println("不加&进行获取内部属性值",a.age)
fmt.Println("加&进行获取内部属性值",b.age)
// 加与不加修改属性值
// 不加&进行修改内部属性值 51
// 加&进行修改内部属性值 51
a.age = 51
b.age = 51
fmt.Println("不加&进行修改内部属性值",a.age)
fmt.Println("加&进行修改内部属性值",b.age)
}
3.分析代码
3.1底层使用了引用传值,上层就会用引用传值
3.2底层没有用就不会使用引用传值
3.3函数调用
4.实际代码
package main
import(
"fmt"
"reflect"
)
type b struct{
c int
d string
}
type bb struct{
ff b
dd string
}
type bbb struct{
// 这句话是在表明
// c是一个引用传值
// b对应地址
c *b
dd string
}
func main(){
a:=1;
// &{变量}=地址 给地址赋值哈
// *{地址}=值 取出地址值哈
fmt.Println(&a); // 0xc042052058
fmt.Println(*&a); // 1
// fmt.Println(*a);//报错 错误的指向 所以说a必须是一个地址 在这个例子中 a不是地址 所以报错
// 这个是结构体的简单引用传值与值传递
// &{变量}-结构体的赋值先要声明哪个类型结构体
g:=&b{c:1,d:"gggg"};
fmt.Println(g);// &{1 gggg}
gg:=b{c:1,d:"gggg"};
fmt.Println(gg);// {1 gggg}
// 这个是结构体嵌套结构体进行值传递
gggg:=bb{ff:b{c:1,d:"gggg"},dd:"gggg"}
fmt.Println(gggg);// {{1 gggg} gggg}
// 这个是结构体嵌套进行引用传值
// 因为此时在bbb中b是一个地址&{}
// c后面是结构体进行赋值 是对
// 此处的b只是针对结构体b的
// *b 所以b为地址 所以传递地址即&b
ggggg:=bbb{c:&b{c:1,d:"gggg"},dd:"gggg"}
fmt.Println(reflect.TypeOf(ggggg.c));//*main.b
fmt.Println(ggggg.c); //&{1 gggg}
fmt.Println(ggggg); // {0xc04204c420 gggg}
// 这个是结构体嵌套进行引用传值
// 这样是错误的哈,在bbb里面b是引用传值 直接用值传递会报错
// cannot use b literal (type b) as type *b in field value
// 不能在字段值中使用b文字(类型b)作为类型*b
// 就是说我们用的是b类型 在结构体声明的时候是*b 所以错误
// gggggg:=bbb{c:b{c:1,d:"gggg"},dd:"gggg"}
// fmt.Println(gggggg); //错误
// invalid indirect of b literal (type b) b字母无法指向b类)
// ggggggg:=bbb{c:*b{c:1,d:"gggg"},dd:"gggg"}
// fmt.Println(ggggggg); //错误
// 结构体的定义要么是值传递(方式一) 要么是引用传递使用* 对类型定义(方式二)
// 结构体的赋值要么是什么值传递(方式一) 要么引用传递-&类型名字{id:1,name:"aa"}(方式二)
// 方式一对应方式一;方式二对应方式二
}
// 0xc042052058
// 1
// &{1 gggg}
// {1 gggg}
// {{1 gggg} gggg}
// *main.b
// &{1 gggg}
// {0xc04204c420 gggg}
5.对应
-
// 结构体的定义要么是值传递(方式一) 要么是引用传递使用* 对类型定义(方式二)
-
// 结构体的赋值要么是什么值传递(方式一) 要么引用传递-&类型名字{id:1,name:"aa"}(方式二)
-
// 方式一对应方式一;方式二对应方式二