【发布时间】:2021-04-29 20:08:29
【问题描述】:
我之前有一个工作面试,被问到以下代码的输出是什么:
struct A {
int data[2];
A(int x, int y) { data[0] = x; data[1] = y; }
virtual void f() {}
};
int main(){
A a(22, 33);
int* data = (int*)&a;
cout << data[2] << endl;
}
我已经讨论过了,但无法完全弄清楚。他提到虚函数是一个提示。之后我编译它并得到了输出:
22
然后我想到了虚函数,把它去掉了:
struct A {
int data[2];
A(int x, int y) { data[0] = x; data[1] = y; }
//virtual void f() {}
};
int main(){
A a(22, 33);
int* data = (int*)&a;
cout << data[2] << endl;
}
导致输出:
0
我的问题是:看似无关紧要的虚函数调用如何影响结果对象的内存布局导致这种情况?
【问题讨论】:
-
data[2]是未定义的行为。无需多言。回答问题 - 对象将有一个 vtable -
你的面试官依赖于他们选择的编译器的行为。面试官可能没有意识到这一点。 C++ 语言在调用未定义行为时很清楚。也就是说,possible dupe.
-
这两种情况下的行为都是未定义的。如果面试官期望特定的输出 - 可能与公司使用的特定编译器一致 - 那么你的面试官是一名黑客,他编写特定于一个编译器的代码,却不了解未定义行为的重要性。
-
也许你申请的工作与逆向工程或黑客有关? Richard Critten 和 Drew Dorman 从“法律”的角度来看都是正确的。 “这是未定义的行为。”是正确答案。然而,已经为那些寻求定义这种“未定义行为”的人创造了很多工作,从这个角度来看,您观察到的行为将几乎在所有现代编译器中普遍存在。正如其他人所指出的,您刚刚发现了 vtables。尝试打印 data[0] 或 data[1] 以查看更多信息。
-
@Roguebantha - 实际上假设 vtable 的大小(或它的表示,在类类型中)是
int大小的两倍。这不是一个有效的普遍假设。如果问题要求对可能适合逆向工程工作的某些观察到的行为(将特定于特定实现)进行可能解释。如果在这两种情况下都需要特定的输出,则表明缺乏理解。
标签: c++ memory virtual-functions