Форум программистов, компьютерный форум, киберфорум
C++
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.82/11: Рейтинг темы: голосов - 11, средняя оценка - 4.82
 Аватар для anapshy
533 / 274 / 220
Регистрация: 14.11.2016
Сообщений: 1,054

Указатель на vftable

04.03.2020, 16:28. Показов 2610. Ответов 6
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Код (х86)
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include <iostream>
#include <iomanip>
#include <Windows.h>
 
class Base
{
public:
    int a = 12;
    virtual ~Base() = default;
    void sMethod1(void) const
    {
        std::cout << "Base::sMethod1" << std::endl;
    }
    virtual void vMethod2(void) const
    {
        std::cout << "Base::vMethod2" << std::endl;
    }
    virtual void vMethod3(void) const
    {
        std::cout << "Base::vMethod3" << std::endl;
    }
};
 
class Derived final : public Base
{
public:
    void sMethod1(void) const
    {
        std::cout << "Derived::sMethod1" << std::endl;
    }
    void vMethod2(void) const override
    {
        std::cout << "Derived::vMethod2" << std::endl;
    }
};
 
void Invoke(const Base *pObj)
{
    std::cout << "\tInvoke for 0x" << (&*pObj) << std::endl;
    
    pObj->sMethod1();
    pObj->vMethod2();
    pObj->vMethod3();
    
    std::cout << std::endl;
}
 
void PrintVftable(const Base* pObj)
{
    std::cout << "\tVftable for 0x" << (&*pObj) << std::endl;
 
    PDWORD pVftable = (PDWORD)(*(PDWORD)pObj);
    for (auto i = 0; i < 3; ++i)
    {
        std::cout << " >> 0x" << (*(pVftable + i)) << std::endl;
    }
 
    std::cout << std::endl;
}
 
int main(void)
{
    Base obj1{};
    Derived obj2{};
 
    std::cout << std::hex;
 
    std::cout << (&(obj1.a)) << std::endl;
    std::cout << "obj1 >> 0x" << (&obj1) << std::endl;
    std::cout << "obj2 >> 0x" << (&obj2) << std::endl;
    std::cout << std::endl;
    
    Invoke(&obj1);
    PrintVftable(&obj1);
    
    Invoke(&obj2);
    PrintVftable(&obj2);
}

Как понять следующее выражение:
C++
52
PDWORD pVftable = (PDWORD)(*(PDWORD)pObj);
1. (PDWORD)pObj - получаем адрес на который ссылается указатель pObj.
2. *(PDWORD)pObj - первые 4 байта объекта по данному адресу (на рисунке случайно выделил 4 ячейки вместо 1-й) obj1 (63 строка кода).
3. (PDWORD)(*(PDWORD)pObj) - ссылаемся на адрес начала объекта obj1.
4. PDWORD pVftable = (PDWORD)(*(PDWORD)pObj) - тут ссылаемся на адрес, который лежит в памяти по адресу определённому в п.3 ?!?!?!
Миниатюры
Указатель на vftable  
0
Лучшие ответы (1)
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
04.03.2020, 16:28
Ответы с готовыми решениями:

Как получить ссылку на указатель или указатель на указатель в массиве?
В процессе реализации сортировки пузырьком натолкнулся на такую проблему: как поменять значения указателей, передаваемых в функцию. Если...

А почему нельзя передавать в ф-ю добавления элемента в стек один указатель? Почему нужен именно указатель на указатель?
Вот код ф-ии добавления элемента в стек: void push1(Node **top, int d) { // top – указатель начала стека Node *pv;...

Функция, получающая указатель на обычную функцию, получает указатель на метод класса
Здравтсвуйте. Имеется вопрос по указателям на методы класса. Допустим, есть функция( f ), которая принимает указатель на функцию и...

6
 Аватар для anapshy
533 / 274 / 220
Регистрация: 14.11.2016
Сообщений: 1,054
04.03.2020, 17:05  [ТС]
Наверное сложно как-то изъяснился. Код работает правильно. Просто не совсем понятен вывод выражения. Почеркал на листочке и пришел к вывод, что pVftable при операции "равно" указывает на виртуальную таблицу. А изначально думал, что будет указывать на адрес 0x11223354

Добавлено через 19 минут
C++
1
2
3
4
5
6
int x = 12;
int* pX = &x;
int* pY = pX;
std::cout << x << std::endl;
std::cout << *pX << std::endl;
std::cout << *pY << std::endl;
Везде выведет 12. Всё. Вопрос снят. Теперь понятно понятно почему pVftable ссылается на 0x11223366. Забыл правило с присваиванием одноуровневых указателей. Адрес то на который указывает указатель A передается указателю B после операции B = A.
0
 Аватар для COKPOWEHEU
4083 / 2681 / 432
Регистрация: 09.09.2017
Сообщений: 11,929
04.03.2020, 17:14
Лучший ответ Сообщение было отмечено anapshy как решение

Решение

Цитата Сообщение от anapshy Посмотреть сообщение
Как понять следующее выражение:
как пример неудачного выбора типов переменных.
Цитата Сообщение от anapshy Посмотреть сообщение
Указатель на vftable
В начале объекта как раз и лежит таблица виртуальных функций. То есть ваш объект Base можно представить как структуру
C++
1
2
3
4
struct BaseStruct{
  void **vftable; //именно так, а вовсе не PDWORD!
  int a;
};
vftable это массив указателей на функции. Для понимания можно представить так:
C++
1
2
3
4
5
typedef void (*virt_func)(Base*);
struct BaseStruct{
  virt_func vftable[];
  int a;
};
Строго говоря, такая запись некорректна, потому что в объекте хранится не массив, а только указатель на массив. Но в качестве псевдокода сойдет.
Как несложно догадаться, адрес первого элемента структуры и адрес самой структуры это одно и то же, то есть
C++
1
2
3
Base obj;
BaseStruct *str = &obj;
str == &(str->vftable);
Таким образом, пунктом 4 вы просто получаете доступ к этому массиву.
Самое забавное, что vftable - переменная, ее значение можно изменить. Тогда класс будет думать, что он, скажем, свой родитель. А можно и вызывать методы вручную:
C++
1
2
3
4
5
typedef void (*virt_func)(Base *);
...
Base obj;
virt_func *func = *( (virt_func**)&obj );
func[0](&obj);
0
 Аватар для anapshy
533 / 274 / 220
Регистрация: 14.11.2016
Сообщений: 1,054
04.03.2020, 18:34  [ТС]
Цитата Сообщение от COKPOWEHEU Посмотреть сообщение
В начале объекта как раз и лежит таблица виртуальных функций. То есть ваш объект Base можно представить как структуру
Это я знаю
Базовый макет класса
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class A
{
    int a1;
public:
    virtual int A_virt1();
    virtual int A_virt2();
    static void A_static1();
    void A_simple1();
};
 
class B
{
    int b1;
    int b2;
public:
    virtual int B_virt1();
    virtual int B_virt2();
};
 
class C: public A, public B
{
    int c1;
public:
    virtual int A_virt2();
    virtual int B_virt2();
};
Макет
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class A size(8):
   +---
0  | {vfptr}
4  | a1
   +---
A's vftable:
0  | &A::A_virt1
4  | &A::A_virt2
 
class B size(12):
   +---
0  | {vfptr}
4  | b1
8  | b2
   +---
B's vftable:
0  | &B::B_virt1
4  | &B::B_virt2
 
class C size(24):
   +---
   | +--- (base class A)
0  | | {vfptr}
4  | | a1
   | +---
   | +--- (base class B)
8  | | {vfptr}
12 | | b1
16 | | b2
   | +---
20 | c1
   +---
C's vftable for A:
0  | &A::A_virt1
4  | &C::A_virt2
C's vftable for B:
0  | &B::B_virt1
4  | &C::B_virt2
Увидел просто подобное выражение и усомнился, что можно таким способ получить доступ к vftable. Начал разбираться и сам запутался. Но ваш способ однозначно в разы читабелен.
0
 Аватар для COKPOWEHEU
4083 / 2681 / 432
Регистрация: 09.09.2017
Сообщений: 11,929
05.03.2020, 11:00
Помедитируйте еще над этим:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include <stdio.h>
#include <string.h>
 
class Base{
public:
  char field[10];
  void method(){ printf("  M: Base  (%s)\n", field); }
  virtual void virt(){ printf("  V: Base  (%s)\n", field); }
  Base(){strcpy(field, "Base");}
  virtual ~Base(){}
};
 
class Child:public Base{
public:
  void method(){ printf("  M: Child (%s)\n", field); }
  virtual void virt(){ printf("  V: Child (%s)\n", field); }
  Child(){strcpy(field, "Child");}
  virtual ~Child(){}
};
 
struct Hack{
  void **vtable;
  char field[10];
};
 
typedef void (*vfunc_vv)(Base*); //виртуальная функция, принимает и возвращает void
 
int main(){
  Base *b = new Base; //эталонный базовый класс
  Child *c = new Child; //эталонный дочерний класс
  Base *v = new Child; //тестовый перегруженный класс
  
  printf("Base\n");
  b->method(); b->virt();
  printf("Child\n");
  c->method(); c->virt();
  printf("Virtual\n");
  v->method(); v->virt();
  printf("\n");
  
  //Осторожно приступаем к хирургии мозга: вскрываем череп и смотрим как оно там шевелится
  Hack *h; //хакерская структура чтобы лезть непосредственно в мозги класса
  vfunc_vv func;
  printf("Base - vtable\n");
  h = (Hack*)b;
  func = ((vfunc_vv)(h->vtable[0]));
  func(b); //вызываем метод как обычную функцию! Обратите внимание, что она принимает неявный (ну, теперь-то явный!) первый параметр - экземпляр класса
  printf("  Field =  [%s]\n", h->field);
  printf("Child - vtable\n");
  h = (Hack*)c;
  func = ((vfunc_vv)(h->vtable[0]));
  func(c);
  printf("  Field =  [%s]\n", h->field);
  printf("Virtual - vtable\n");
  h = (Hack*)v;
  func = ((vfunc_vv)(h->vtable[0]));
  func(v);
  printf("  Field =  [%s]\n", h->field);
  printf("\n");
  
  //а вот теперь начинаем веселье! Пересадка мозга!
  ((Hack*)v) -> vtable = ((Hack*)b)->vtable; //от базового к виртуальному
  ((Hack*)b) -> vtable = ((Hack*)c)->vtable; //от дочернего к базовому
  ((Hack*)c) -> vtable = ((Hack*)v)->vtable; //от виртуального (у него уже базовый) к дочернему
  printf("Base <-- Child\n");
  b->method(); b->virt();
  printf("Child <-- Virt (Base)\n");
  c->method(); c->virt();
  printf("Virt <-- Base\n");
  v->method(); v->virt();
  
  //если операционная уцелела после наших экспериментов - прибираемся за собой
  delete v;
  delete b;
  delete c;
}
1
 Аватар для anapshy
533 / 274 / 220
Регистрация: 14.11.2016
Сообщений: 1,054
05.03.2020, 14:58  [ТС]
COKPOWEHEU, ... ещё добавить, чтобы методы с параметрами можно было вызывать))
C++
26
typedef void (*vfunc_vv)(Base*, ...)
0
 Аватар для COKPOWEHEU
4083 / 2681 / 432
Регистрация: 09.09.2017
Сообщений: 11,929
05.03.2020, 16:32
anapshy, Нет, так делать не надо. Если вы знаете, что параметр должен принимать, напишите соответствующий прототип функции. Если нет - просто не вызывайте.
Например, у вас есть класс:
C
1
2
3
4
5
6
class Base{
public:
  virtual void func1(){...}
  virtual int func2(int x, int y){...}
  virtual char* func3(char *fmt, ...){...}
};
Вот и опишите функции:
C
1
2
3
typedef void (*vfunc1)(Base*);
typedef int (*vfunc2)(Base*, int, int);
typedef char* (*vfunc)(Base*, char*, ...);
И приводите именно к ним. Вызывать неизвестно что неизвестно как - плохая идея.
Впрочем, использовать такую "микрохирургию мозга С++" на практике - идея не лучше. Вот в порядке изучения и извращения уже другое дело.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
05.03.2020, 16:32
Помогаю со студенческими работами здесь

Функция, принимающая указатель и число байт и выделяющая память под указатель
Здравствуйте. Задача легкая, но почему-то завис Нужно написать функцию, принимающую указатель и число байт и выделяющую память под...

Указатель типа void. Использование косвенного связывания через универсальный указатель
Необходимо использовать косвенного связывания через универсальный указатель, примерный вид: struct обобщение{ключ; void* на что угодно}...

Как правильно удалять выделенную память под указатель на указатель?
есть код #include &lt;iostream&gt; #include &lt;conio.h&gt; #include &lt;stdlib.h&gt; #include &lt;time.h&gt; using namespace std; void sort_1(const...

Указатель на функцию, которая принимает в качестве параметра указатель на массив
я не понимаю. вроде делаю правильно, но выходит ошибка. есть функция. int foo(int *mas){}; cоздаю на нее указатель. int...

Зачем нужен указатель на указатель при работе с однонаправленным списком?
День добрый. Столкнулся с непониманием этой темы. В частности, совершенно непонятен алгоритм инициализации такого вот списка. Например:...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
7
Ответ Создать тему
Новые блоги и статьи
Установка Qt Creator для C и C++: ставим среду, CMake и MinGW без фреймворка Qt
8Observer8 05.04.2026
Среду разработки Qt Creator можно установить без фреймворка Qt. Есть отдельный репозиторий для этой среды: https:/ / github. com/ qt-creator/ qt-creator, где можно скачать установщик, на вкладке Releases:. . .
AkelPad-скрипты, структуры, и немного лирики..
testuser2 05.04.2026
Такая программа, как AkelPad существует уже давно, и также давно существуют скрипты под нее. Тем не менее, прога живет, периодически что-то не спеша дополняется, улучшается. Что меня в первую очередь. . .
Отображение реквизитов в документе по условию и контроль их заполнения
Maks 04.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа "ПланированиеСпецтехники", разработанного в конфигурации КА2. Данный документ берёт данные из другого нетипового документа. . .
Фото всей Земли с борта корабля Orion миссии Artemis II
kumehtar 04.04.2026
Это первое подобное фото сделанное человеком за 50 лет. Снимок называют новым вариантом легендарной фотографии «The Blue Marble» 1972 года, сделанной с борта корабля «Аполлон-17». Новое фото. . .
Вывод диалогового окна перед закрытием, если документ не проведён
Maks 04.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа "СписаниеМатериалов", разработанного в конфигурации КА2. Задача: реализовать программный контроль на предмет проведения документа. . .
Программный контроль заполнения реквизитов табличной части документа
Maks 02.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа "СписаниеМатериалов", разработанного в конфигурации КА2. Задача: 1. Реализовать контроль заполнения реквизита. . .
wmic не является внутренней или внешней командой
Maks 02.04.2026
Решение: DISM / Online / Add-Capability / CapabilityName:WMIC~~~~ Отсюда: https:/ / winitpro. ru/ index. php/ 2025/ 02/ 14/ komanda-wmic-ne-naydena/
Программная установка даты и запрет ее изменения
Maks 02.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа "СписаниеМатериалов", разработанного в конфигурации КА2. Задача: при создании документов установить период списания автоматически. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru