Visual C 是如何实现的?编译器将 this ptr 传递给被调用函数?

发布于 2024-11-26 01:48:50 字数 1706 浏览 4 评论 0原文

我正在使用 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

猫七 2024-12-03 01:48:50

Visual Studio 中的成员函数有一个特殊的调用约定,__thiscall,其中 this 在特殊寄存器中传递。是哪一个,我不记得了,但是 MSDN 会说。如果你想调用虚函数表中的函数指针,你将不得不进入汇编程序。

当然,您的代码表现出大量未定义的行为 - 只能使用 charunsigned char 指针为对象添加别名,而绝对不是 int > 指针 - 甚至忽略整个 vtable 假设。

Member functions in Visual Studio have a special calling convention, __thiscall, where this 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 or unsigned char pointer, and definitely not an int pointer- even ignoring the whole vtable assumptions thing.

这个俗人 2024-12-03 01:48:50

好的,使用 DeadMG 的提示我找到了一种不使用汇编器的方法:

1) 从 fg[] 数组中的函数中删除 ByteExaminer* arg
2) 添加一个函数 void callfunc(void (*)()); 到 ByteExaminer:

void ByteExaminer::callfunc(void (*func)())
{
    func();
}

...这显然是有效的,因为 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:

void ByteExaminer::callfunc(void (*func)())
{
    func();
}

... 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文