Visual C 是如何实现的?编译器将 this ptr 传递给被调用函数?
我正在使用 Eckel 的“Thinking in C++”来学习 C++。它声明如下:
- 如果一个类包含虚方法,则为该类创建一个虚函数表,等等。函数表的工作原理被粗略地解释。 (我知道 vtable 不是必需的,但 Visual C++ 创建了一个。)
- 调用对象作为参数传递给被调用函数。 (这对于 Visual C++(或任何编译器)来说可能并非如此。)我试图找出 VC++ 如何将调用对象传递给函数。
为了在 Visual C++ 中测试这两点,我创建了以下类(使用 Visual Studio 2010、WinXP Home 32 位):
ByteExaminer.h:
#pragma once
class ByteExaminer
{
public:
short b[2];
ByteExaminer(void);
virtual void f() const;
virtual void g() const;
void bruteFG();
};
ByteExaminer.cpp:
#include "StdAfx.h"
#include "ByteExaminer.h"
using namespace std;
ByteExaminer::ByteExaminer(void)
{
b[0] = 25;
b[1] = 26;
}
void ByteExaminer::f(void) const
{
cout << "virtual f(); b[0]: " << hex << b[0] << endl;
}
void ByteExaminer::g(void) const
{
cout << "virtual g(); b[1]: " << hex << b[1] << endl;
}
void ByteExaminer::bruteFG(void)
{
int *mem = reinterpret_cast<int*>(this);
void (*fg[])(ByteExaminer*) = { (void (*)(ByteExaminer*))(*((int *)*mem)), (void (*)(ByteExaminer*))(*((int *)(*mem + 4))) };
fg[0](this);
fg[1](this);
}
通过 bruteFG()
中的 vtable 进行导航有效 - 当我调用 fg[0](this)
时,会调用 f()
。然而,不起作用的是将 this
传递给函数 - 这意味着 this->b[0]
未正确打印(而是出现垃圾。我实际上很幸运,这不会产生段错误)。
所以实际的输出
ByteExaminer be;
be.bruteFG();
是:
virtual f(); b[0]: 1307
virtual g(); b[1]: 0
那么我应该如何继续获得正确的结果? this
指针如何传递给 VC++ 中的函数?
(注意:我永远不会认真地以这种方式编程。这是“为了乐趣”;或者是为了学习经验。所以不要试图将我转变为正确的 C++ 风格:))
I'm learning C++ using Eckel's "Thinking in C++". It states the following:
- If a class contains virtual methods, a virtual function table is created for that class etc. The workings of the function table are explained roughly. (I know a vtable is not mandatory, but Visual C++ creates one.)
- The calling object is passed to the called function as an argument. (This might not be true for Visual C++ (or any compiler).) I'm trying to find out how VC++ passes the calling object to the function.
To test both points in Visual C++, I've created the following class (using Visual Studio 2010, WinXP Home 32bit):
ByteExaminer.h:
#pragma once
class ByteExaminer
{
public:
short b[2];
ByteExaminer(void);
virtual void f() const;
virtual void g() const;
void bruteFG();
};
ByteExaminer.cpp:
#include "StdAfx.h"
#include "ByteExaminer.h"
using namespace std;
ByteExaminer::ByteExaminer(void)
{
b[0] = 25;
b[1] = 26;
}
void ByteExaminer::f(void) const
{
cout << "virtual f(); b[0]: " << hex << b[0] << endl;
}
void ByteExaminer::g(void) const
{
cout << "virtual g(); b[1]: " << hex << b[1] << endl;
}
void ByteExaminer::bruteFG(void)
{
int *mem = reinterpret_cast<int*>(this);
void (*fg[])(ByteExaminer*) = { (void (*)(ByteExaminer*))(*((int *)*mem)), (void (*)(ByteExaminer*))(*((int *)(*mem + 4))) };
fg[0](this);
fg[1](this);
}
The navigation through the vtable in bruteFG()
works - when I call fg[0](this)
, f()
is called. What does NOT work, however, is the passing of this
to the function - meaning that this->b[0]
is not printed correctly (garbage comes out instead. I'm actually lucky this doesn't produce a segfault).
So the actual output for
ByteExaminer be;
be.bruteFG();
is:
virtual f(); b[0]: 1307
virtual g(); b[1]: 0
So how should I proceed to get the correct result? How are the this
pointers passed to functions in VC++?
(Nota bene: I'm NOT going to program this way seriously, ever. This is "for the lulz"; or for the learning experience. So don't try to convert me to proper C++ianity :))
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
Visual Studio 中的成员函数有一个特殊的调用约定,
__thiscall
,其中this
在特殊寄存器中传递。是哪一个,我不记得了,但是 MSDN 会说。如果你想调用虚函数表中的函数指针,你将不得不进入汇编程序。当然,您的代码表现出大量未定义的行为 - 只能使用
char
或unsigned char
指针为对象添加别名,而绝对不是int
> 指针 - 甚至忽略整个 vtable 假设。Member functions in Visual Studio have a special calling convention,
__thiscall
, wherethis
is passed in a special register. Which one, I don't recall, but MSDN will say. You will have to go down to assembler if you want to call a function pointer which is in a vtable.Of course, your code exhibits massively undefined behaviour- it's only OK to alias an object using a
char
orunsigned char
pointer, and definitely not anint
pointer- even ignoring the whole vtable assumptions thing.好的,使用 DeadMG 的提示我找到了一种不使用汇编器的方法:
1) 从 fg[] 数组中的函数中删除 ByteExaminer* arg
2) 添加一个函数
void callfunc(void (*)());
到 ByteExaminer:...这显然是有效的,因为 func() 是 callfunc 中第一个使用的东西,所以
ecx
显然之前没有改变。但这是一个肮脏的伎俩(正如您在上面的代码中看到的,我总是在寻找干净的代码)。我仍在寻找更好的方法。OK using DeadMG's hint I've found a way without using assembler:
1) Remove the ByteExaminer* arg from the functions in the fg[] array
2) Add a function
void callfunc(void (*)());
to ByteExaminer:... this apparently works because func() is the first thing to be used in callfunc, so
ecx
is apparently not changed before. But this is a dirty trick (as you can see in the code above, I'm always on the hunt for clean code). I'm still looking for better ways.