刚接触go没有多久;今天看关于 “结构体字面量时产生几个问题想讨论一下,也算是一种记录”
第一个问题:
type Point struct{X,Y float64}
p := &Point{1, 2}
产生疑惑的原因: Point{1,2} 算是字面量,字面量基本和常量一样不能取地址,为什么这个可以?
b := &1 //不能对常量取地址(常量为什么不能取地址?☺)
//为什么常量不能取地址: b := &1 可认为把1放在b代表的内存地址中,而计算机中的数字是用0,1 序列的补码表示的. 而数字1的产生是由一些“门电路产生的高低电平表示的” ,这是数字产生的时候你怎么能取地址那。
自己的想法 : 大部分字面量和常量差不多; sturct之所以能够取地址大概因为 struct在 内存是连续存储的, 作用类似一个变量
第二个问题:
type Point struct{X,Y float64}
func pot(x,y float64) Point {
return Point{x,y}
}
func pt(x,y float64) *Point {
return &Point{x,y}
}
func main() {
pot(2,3).X = 4 // cannot assign to point(2, 3).X
pt(2,3).X = 4 //ok
}
产生疑惑的原因: 如果返回是 *Point指针类型,pt函数中的变量Point会分配在堆上,Point作用域扩大, 所以修改值没有问题;
但是如果返回 Point 为什么再次修改值就不行?
自己的想法 : 仔细看代码 pot(2,3).X = 4 执行.X 操作的时候这时;Point类型的值还是在局部函数的栈中,这时已从pot函数返回 Point类型值可能在随后某个时间会被销毁,如果再次执行.X ;这时可能Point值地址已被回收.
期待您的回答是这样的:
1.是自己的想法,这样可以讨论
2.由于上面的想法都是根据结果猜想的,可能存在错误,片面,缺少依据; 如有你的想法有理有据,有参考来源会更好
第一个问题往深了说就是编译原理和 CPU 和内存硬件的事。往浅了说呢,有下面这么几个计算机的基础知识需要了解:
那进程地址空间又是什么呢?是每个进程都有独立的一块虚拟内存模型,和物理内存建立的映射关系。
最后,程序中的变量在运行时会变成一个“寄存器+偏移量”的形式存在。偏移量的事情可以先忽略。建议你去了解一下 C 语言中栈帧(stack frame)的概念。有兴趣可以进一步学个汇编和编译器。
结论:变量名就是某一个地址的标识符,你可以简单地理解为像windows系统里面的一个快捷方式。取地址操作就是通过这个快捷方式找到它在进程地址空间里真实的地址。
但是为什么不能对它取地址,写成 &8 呢?因为它本身就是实体!而不是快捷方式!
从实际执行来看,这个数字是没有寻址操作的,它会被直接使用,当它所在的那条指令执行完成,它就失去作用了。你也就没有保存它地址的意义。
回答你的问题:
p := &Point{1, 2}
叫复合字面量,Point{1, 2}
本身并不是实体,它也是某一段地址的快捷方式,所以可以对它进行取地址操作如&Point{1,2}
。然后留个思考题:对于字符串字面量,C 语言可以取地址,但是 go 不允许。可以想想主要是这两门语言的哪部分实现差异影响了结果。^_^
em。。。第二个问题,你为什么会想到这么写呢。。这个思路我猜不透哎。虽然你的想法大体上是对的,但是
pt(2,3).X = 4
这样写实际是没有意义的。因为一旦这行代码执行完,你再也没有办法用这个结果了,如果再调一次 pt(),又是一个新的对象了。go 有 gc 还好说,如果在 c 里这么写的话,就是内存泄漏啊。。