Как компилятор Visual C ++ передает this ptr вызываемой функции? - PullRequest
3 голосов
/ 23 июля 2011

Я изучаю C ++, используя «мышление в C ++» Экеля. В нем говорится следующее:

  • Если класс содержит виртуальные методы, для этого класса создается таблица виртуальных функций и т. Д. Работа таблицы функций поясняется примерно. (Я знаю, что vtable не является обязательным, но Visual C ++ создает его.)
  • Вызывающий объект передается вызываемой функции в качестве аргумента. (Это может быть не так для Visual C ++ (или любого компилятора).) Я пытаюсь выяснить, как VC ++ передает вызывающий объект в функцию.

Чтобы проверить обе точки в Visual C ++, я создал следующий класс (с использованием 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);
}

Навигация по vtable в bruteFG() работает - когда я звоню fg[0](this), вызывается f(). Однако, что НЕ работает, так это передача this функции - это означает, что this->b[0] печатается неправильно (вместо этого появляется мусор. Мне действительно повезло, что это не приводит к segfault).

То есть фактический результат для

ByteExaminer be;
be.bruteFG();

есть:

virtual f(); b[0]: 1307
virtual g(); b[1]: 0

Так как мне поступить, чтобы получить правильный результат? Как указатели this передаются в функции в VC ++?

(Примечание: я не собираюсь так серьезно программировать, никогда. Это "для лулзов"; или для обучения. Поэтому не пытайтесь преобразовать меня в правильный язык С ++ :) )

Ответы [ 2 ]

4 голосов
/ 23 июля 2011

Функции-члены в Visual Studio имеют специальное соглашение о вызовах, __thiscall, где this передается в специальном регистре. Какой из них, я не помню, но MSDN скажет. Вам нужно будет перейти к ассемблеру, если вы хотите вызвать указатель функции, который находится в vtable.

Конечно, ваш код демонстрирует крайне неопределенное поведение - это нормально, если псевдоним объекта использовать указатель char или unsigned char, и определенно не указатель int - даже игнорируя все предположения vtable. *

0 голосов
/ 23 июля 2011

ОК, используя подсказку DeadMG. Я нашел способ без использования ассемблера:

1) Удалите аргумент ByteExaminer * из функций в массиве fg []. 2) Добавьте функцию void callfunc(void (*)()); в ByteExaminer:

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

... это, очевидно, работает, потому что func () - это первое, что используется в callfunc, поэтому ecx, видимо, раньше не менялось. Но это подвох (как видно из кода выше, я всегда нахожусь в поиске чистого кода). Я все еще ищу лучшие пути.

...