Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > C/C++ программирование > Общие вопросы C/C++
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 25.02.2021, 21:53   #1
apofioz
Форумчанин
 
Регистрация: 22.01.2014
Сообщений: 313
По умолчанию Поиск объекта памяти.

Здравствуйте! И опять вопрос:
Цитата:
Создайте класс, содержащий переменные и виртуальные функции. Напи*шите функцию, которая ищет в памяти объект вашего класса и выводит его различные фрагменты. Для этого вам придется поэкспериментировать и опре*делить, где в объекте хранятся указатели VPTR.
Код:
class A
{
public:
A() :ch('F'), iv(1), lv(12L), fv(2.71f), dv(3.141592) { }
virtual void f1() { printf("%s %f \n", "This double : ", dv); }
virtual void f2() { printf("%s %c \n", "This character: ", ch); }
//private:
char ch;
int iv;
long lv;
float fv;
double dv;
};
Функция, которую я написал. В main() я через new создаю объект класс и передаю его в функцию. Но как получить доступ к _vftable/vptr ума не приложу, хотя бы как вообще определить, что это _vftable, а не объект поля класса или виртуальная функция? Как вывести содержимое участка памяти, которое, как я предполагаю, отведено под мой класс.
Исхожу из того, что указатель указывает на начало класса, соответственно, если к началу прибавить размер класса, то я попадаю в конец отведённой под мой класс памяти.
Почему размер класса равен 32? ведь 1 + 4 + 4 + 4 + 8 + 4 = 25, а не 32, не ясно.

Код:
void func(A* f)
{
printf("%s %i \n", "sizeof(A) = ", sizeof(*f));
printf("%p \n", &*f);
cout << ((&*f) + 0) << endl;
cout << ((&*f) + 4) << endl;
cout << ((&*f) + 5) << endl;
cout << ((&*f) + sizeof(*f)) << endl;
}

int main()
{
A* aa = new A;
func(aa);

_getch();
return 0;
}
Разве может С++ работать с памятью напрямую, кроме new, delete мне ничего неизвестно!
apofioz вне форума Ответить с цитированием
Старый 26.02.2021, 07:47   #2
Алексей1153
фрилансер
Форумчанин
 
Регистрация: 11.10.2019
Сообщений: 960
По умолчанию

apofioz, 32 - это из-за выравнивания. Чтобы не было выравнивания (хотя, зачем это тут) можно выставить выравнивание

Код:
#pragma pack(push,1)
... тут описать класс, выравнивание будет == 1
#pragma pack(pop)
место размещения указателя на виртуальную таблицу вроде бы не задаётся стандартом (могу ошибаться), но обычно этот указатель в начале экземпляра класса. "Визуализировать" можно так:

Код:
#include <iostream>
#include <iomanip>
using std::cin;
using std::cout;

class A
{
    virtual void f1() {}
    virtual void f2() {}
};

//без выравнивания
class B:public A
{
    public:
    
    uint8_t  v1{0x11};
    uint32_t v3{0x33333333};
    uint16_t v2{0x2222};

    void f1()override {}
    void f2()override {}
};

//с выравниванием
#pragma pack(push,1)
class C:public A
{
    public:
    
    uint8_t  v1{0x11};
    uint32_t v3{0x33333333};
    uint16_t v2{0x2222};

    void f1()override {}
    void f2()override {}
};
#pragma pack(pop)

int main()
{
    A a;
    B b;
    C c;
    
    auto print_hex=[](const char* name,const auto& x)
    {
        cout<<'\n';
        cout<<name<<":"<<'\n';
        auto beg=(const uint8_t*) &x;
        auto end=(const uint8_t*)(&x+1);
        cout<<std::hex<<std::setfill('0');
        for(auto* p=beg; p!=end; p++)
        {
            cout<<std::setw(2)<<(uint16_t)*p<<' ';
        }
    };

    print_hex("a",a);
    print_hex("b",b);
    print_hex("c",c);

    return 0;
}
Код:
a:                                                                                                                                        
30 10 40 00 00 00 00 00                                                                                                                   
b:                                                                                                                                        
10 10 40 00 00 00 00 00 11 09 40 00 33 33 33 33 22 22 13 12 fd 7f 00 00                                                                   
c:                                                                                                                                        
f0 0f 40 00 00 00 00 00 11 33 33 33 33 22 22
в автоматически выровненном экземпляре промежутки между полями заполнены мусором. Указатель оказался 64-битным (компилятор 64 бита)

можно "спровоцировать" более компактное размещение переупорядочиванием полей и без прагмы. Например, если самое длинное поле у нас 32 бита, то можно его разместить первым, а 16 и 8 - после него. Тогда паддинг будет всего 1 байт

Последний раз редактировалось Алексей1153; 26.02.2021 в 07:53.
Алексей1153 вне форума Ответить с цитированием
Старый 26.02.2021, 12:34   #3
apofioz
Форумчанин
 
Регистрация: 22.01.2014
Сообщений: 313
По умолчанию

Цитата:
Сообщение от Алексей1153 Посмотреть сообщение
Чтобы не было выравнивания (хотя, зачем это тут)
Это я спросил ради любопытства, когда получил результат размера класса, через sizeof(), то сомнения одолели, что там добавляет ещё компилятор, откуда такой размер...

Цитата:
#pragma pack(push,1)
Тут совсем глухо, я об этом даже и не читал ещё. Что такое выравнивание знаю, если не ошибаюсь это делает с целью оптимизации.

Цитата:
Сообщение от Алексей1153 Посмотреть сообщение
auto print_hex=[](const char* name,const auto& x)
Это что за конструкция, функтор? Альтернативный стиль это написание функции для каждого типа?
(&x + 1) почему '+1' и точно попадаете на конец структуры?

Почему бы тогда не написать что то такое:
Код:
void print_hex1(const char* name, const B& var)
{
	cout << "\n" << name << ':' << "\n";
	unsigned int sz = sizeof(var);
	const unsigned char* b = (const unsigned char*)&var;
	cout << std::hex << std::setfill('0');
	for (; sz-- > 0;) //прогуляемся по памяти, от начала выделенной области (var) до её (sizeof(var)) конца, с шагом 1. 
	{
		cout << std::setw(2) << (unsigned short int)*b++ << ' ';
	}
}
Не так изящно, но всё же.

Всё равно, спасибо, я бы сам до этого не дошёл. Значит напрямую с памятью работать нельзя, по крайней мере в С++, а если ассемблер подключить?

Последний раз редактировалось apofioz; 26.02.2021 в 16:22.
apofioz вне форума Ответить с цитированием
Старый 26.02.2021, 16:06   #4
Алексей1153
фрилансер
Форумчанин
 
Регистрация: 11.10.2019
Сообщений: 960
По умолчанию

Цитата:
Сообщение от apofioz Посмотреть сообщение
Это что за конструкция
лямбда-выражение

можно заменить на аналогичную шаблонную функцию, но в данном случае мне лямбда показалась удобнее

Цитата:
Сообщение от apofioz Посмотреть сообщение
Почему бы тогда не написать что то такое:
тут нельзя будет подставить любой тип. Да и цикл какой-то нечитабельный. А каст адреса константной переменной к неконстантному (unsigned char*) - это совсем плохо

Цитата:
Сообщение от apofioz Посмотреть сообщение
Значит напрямую с памятью работать нельзя,
этот код и так "работает напрямую с памятью" (внутри лямбды print_hex)

или что ты имеешь в виду под работой с памятью?
Алексей1153 вне форума Ответить с цитированием
Старый 26.02.2021, 16:07   #5
Алексей1153
фрилансер
Форумчанин
 
Регистрация: 11.10.2019
Сообщений: 960
По умолчанию

Цитата:
Сообщение от apofioz Посмотреть сообщение
(&x + 1) почему '+1'
потому что адресная арифметика
Алексей1153 вне форума Ответить с цитированием
Старый 26.02.2021, 16:23   #6
apofioz
Форумчанин
 
Регистрация: 22.01.2014
Сообщений: 313
По умолчанию

Цитата:
Сообщение от Алексей1153 Посмотреть сообщение
этот код и так "работает напрямую с памятью" (внутри лямбды print_hex)
Это память не совсем память это искусственно созданная память для данного эксперимента. Мы же не можем найти по определённому адресу таблицу виртуальных функций...


Цитата:
Сообщение от Алексей1153 Посмотреть сообщение
или что ты имеешь в виду под работой с памятью?
Имею ввиду работу с памятью на нижнем уровне, насколько возможно, конечно, где можно получить доступ к реальным объектам в реальной памяти, по реальным адресам. Как-то так.


Цитата:
Сообщение от Алексей1153 Посмотреть сообщение
потому что адресная арифметика
В курсе, что арифметика. Почему тогда + 1 указывает на конец данной области.
Код:
addr + sizeof(type) * n;
разве не так.


Цитата:
Сообщение от Алексей1153 Посмотреть сообщение
тут нельзя будет подставить любой тип. Да и цикл какой-то нечитабельный. А каст адреса константной переменной к неконстантному (unsigned char*) - это совсем плохо
Теперь лучше.
apofioz вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Адрес объекта в памяти Artempokrowski Общие вопросы Delphi 13 09.04.2012 20:37
[C#] Поиск объекта по образцу OverDosser Фриланс 1 06.11.2011 21:22
ПОиск объекта на изображении TacoBell Qt и кроссплатформенное программирование С/С++ 7 26.10.2010 13:47
Поиск объекта на картинке Terran Общие вопросы Delphi 3 21.05.2010 16:35
Как изменять свойства и функцыи объекта в памяти? VintProg Общие вопросы Delphi 2 10.07.2009 15:29