当编译器看到这段代码时:
SomeClass foo;
int x = foo.bar;
检索 bar 的值时,它的过程是什么?即它会看一些代表类定义的数据结构吗?如果是这样的数据结构是在编译时还是在运行时生成的?
答案 0 :(得分:6)
编译器的地址为foo
。在该地址,有足够的空间用于成员变量(sizeof(SomeClass)
),其中可能包含一些填充。
它知道`bar在类中的某个位置(通常是它们被声明的顺序,加上一些其他魔法,如继承),然后跳转到该偏移量。
那是:
struct SomeClass
{
short s;
float f;
int bar;
char *c;
}
// pseudo-code:
&SomeClass.bar == (&SomeClass) + sizeof(short) + sizeof(float);
在运行时,它获取该数据,并将其分配给x
答案 1 :(得分:4)
在编译时,编译器将有一些数据结构,告诉它如何访问SomeClass的每个成员。对于简单的情况,它只是一个偏移量,但如果你有非平凡的继承,它可能会有更多。
为了处理表达式,编译器会查询此内部数据并(最终)发出相应的机器代码。通过运行时,这个结构将被抛弃,剩下的就是从foo的地址开始,为了做任何需要而发出的代码。但是,如果你有一个指向bar的成员指针,那么如何访问bar成员的细节以某种方式封装在该指针值中(可能是一个偏移量,可能更复杂)。
答案 2 :(得分:2)
当编译器看到SomeClass
的定义时,该过程开始。根据该定义,它构建一个内部结构,其中包含SomeClass
中字段的类型,以及SomeClass
方法的代码位置。
当您编写SomeClass foo;
时,编译器会找到与SomeClass
的构造函数对应的代码,并创建调用该代码的机器指令。在下一行,你写int x = foo.bar
。这里编译器编写机器指令以为int
分配堆栈空间,然后查看其SomeClass
的数据结构。该数据结构将告诉它bar
对象开头的foo
的字节偏移量。然后编译器写入机器代码,将对应bar
的字节复制到x
的内存中。所有这些机器代码都会写入您的可执行文件。
通常,编译完成后,表示SomeClass
和其他定义的数据结构将被丢弃。你剩下的只是一套机器指令。当您实际运行程序时执行这些指令,以便SomeClass
的构造函数和将foo.bar
复制到x
的代码由CPU执行任何关于对象结构的明确知识。
这是一般情况。当您在调试器下运行代码并进行优化时,有一些特殊情况,但这通常是 会发生什么。
答案 3 :(得分:1)
你必须认为在编译期间每个类都变成了一个结构(为了简化说明),所以如果你有
class Foo
{
int x, y, z;
char bar[10];
... etc ...
}
它们被转换为具有指定大小的结构,在这种情况下为4 * 3 + 10个字节。然后它根据对齐方式以更方便的方式排列它们,记住例如在偏移4处你可以找到属性y,而在地址8你可以找到z。
然后这很简单,只需将4添加到分配所涉及的类的地址中,然后获取y的地址,依此类推。
答案 4 :(得分:1)
编译器仅在编译时存储此类类元数据。你的第一个问题是,它如何检索bar的值,实际上非常复杂。您可以将其视为计算bar与对象foo的偏移量,然后读取该位置的内存。然而,根据x的实际使用方式,它可以做一些不同的事情。在某些情况下,“x”可能根本不会出现在已编译的代码中。