我一直想知道编译器如何看到指向结构的指针(在C中假设)和结构本身的真正区别。
struct person p;
struct person *pp;
pp->age
,我总是想象编译器会这样做:“pp的值+结构中的属性”age“的偏移量。”
但它对person.p
的作用是什么?它几乎是一样的。对我来说,“程序员”,p不是内存地址,它就像“结构本身”,但当然这不是编译器如何处理它。
我的猜测是它更像是一种语法,编译器总是(&p)->age
。
我是对的?
答案 0 :(得分:28)
p->q
基本上是(*p).q
的语法糖,因为它取消引用指针p
,然后转到其中的正确字段q
。它为一个非常常见的情况(指向结构的指针)保存了类型。
实质上,->
执行两个 deferences(指针取消引用,字段取消引用),而.
仅执行一个(字段取消引用)。
由于多重引用因子,->
不能被编译器完全替换为静态地址,并且始终至少包括地址计算(指针可以在运行时动态更改,因此位置也会更改),而在某些情况下,.
操作可以被编译器替换为访问固定地址(因为基础结构的地址也可以修复)。
答案 1 :(得分:5)
已更新 (请参阅评论):
你有正确的想法,但只有对于全局变量和静态变量有一个重要区别:当编译器看到全局或静态变量的p.age
时,它可以替换它,在编译时,使用结构中age
字段的确切地址。
相反,pp->age
必须编译为“pp的值+ age
字段的偏移量”,因为pp的值可以在运行时更改。
答案 2 :(得分:3)
即使从“编译器角度”看,这两个语句也不相同。声明p.age
转换为p
的地址+ age
的偏移量,而pp->age
转换为 {{1}中包含的地址 } + pp
的偏移量。
变量的地址和 a(指针)变量中包含的地址是非常不同的东西。
假设年龄的偏移量为5.如果age
是结构,则其地址可能为100,因此p
引用地址105。
但如果p.age
是指向结构的指针,则其地址可能为100,但存储在地址100的值不是pp
结构的开头,而是指针。因此,地址100处的值( person
中包含的地址)可能是250.在这种情况下,pp
引用地址255,而不是105。
答案 3 :(得分:1)
由于p是本地(自动)变量,因此它存储在堆栈中。因此,编译器在关于堆栈指针(SP)或帧指针(FP或BP,在其存在的体系结构中)的偏移方面访问它。相反,* p指的是堆中分配的内存地址[通常],因此不使用堆栈寄存器。
答案 4 :(得分:1)
这是我一直问自己的问题。
v.x
,成员运算符,对于结构只有 。
指针运算符的成员v->x
对于struct指针有效 。
那么为什么有两个不同的运营商,因为只需要一个?例如,只能使用.
运算符;编译器总是知道v
的类型,因此它知道该怎么做:v.x
如果v
是结构,(*v).x
如果v
是结构指针
我有三个理论:
不幸的是,我不知道哪一个(如果有的话)是真的。
答案 5 :(得分:0)
在这两种情况下,结构及其成员都通过
来解决地址(人)+抵消(年龄)
将p与存储在堆栈内存中的结构一起使用可为编译器提供更多选项来优化内存使用。它可以只存储年龄,而不是整个结构,如果没有使用其他东西 - 这使得使用上述函数的寻址失败(我认为读取结构的地址会停止此优化)。
堆栈上的结构可能根本没有内存地址。如果结构足够小并且只有很短的时间,它可以映射到一些处理器寄存器(与上面的优化相同,用于读取地址)。
简短的回答:当编译器没有优化时,你是对的。一旦编译器开始仅优化c标准指定的保证。
编辑:删除了“pp->”的有缺陷的堆栈/堆位置因为指向struct可以在堆和堆栈上。