Как ассемблер обрабатывает классы и объекты и как они хранятся в оперативной памяти и исполняемом файле? - PullRequest
2 голосов
/ 22 ноября 2010

Как ассемблер обрабатывает классы и объекты при компиляции программы?И как это хранится в ОЗУ и исполняемом файле?

Сначала память выделяется в соответствии с размером класса, например, 20 байтов.В этих 20 байтах хранятся все переменные класса.Но есть две проблемы:

  1. Как работают указатели на классы?
  2. Как вызываемый метод узнает, какому объекту он принадлежит?1011 *

    Не могли бы вы объяснить это мне?(При использовании примера кода я предпочитаю Objective-C, C ++ и сборку x86)

Ответы [ 2 ]

4 голосов
/ 22 ноября 2010

Ассемблер не имеет ни малейшего представления о том, что такое класс, он только собирает машинный код, в который время от времени добавляется макрос. Для всех целей и задач класс является просто структурой с необязательной vftable, со всей обработкой и специальным классом.функции (виртуализм, полиморфизм, наследственность и т. д.) выполняются на промежуточной стадии, когда создается ИК-код.Память будет выделяться так же, как структура, переменная, массив или любой другой «блоб» данных (статически или динамически, с учетом выравнивания, константности и упаковки), за исключением кода поддержки для обработки стека и статического раскручивания dtor(выполняется снова на уровне IR), ctors и статическая инициализация (хотя статическая инициализация может происходить не только для объектов класса).Я предлагаю вам прочитать книгу о драконах (первые восемь глав будут ее освещать), чтобы получить более четкое представление о том, как работают компилятор и ассемблер, поскольку эти вещи обрабатываются не ассемблером, а фронтом компилятора и/ или серверные части, в зависимости от структуры компилятора и его IL.

3 голосов
/ 22 ноября 2010

(2) Функции-члены переписываются компилятором. Рассмотрим class A следующим образом.

class A {
    int i;
public:
    A () : i (0) { }

    void f (int a, char *b) { i = a; }
}

Тогда то, что делает компилятор из A::f, выглядит примерно так (псевдокод):

void A::f (A * const this, int a, char *b) { this->i = a; }

Рассмотрим теперь вызов A::f.

A a;
a.f (25, "");

Компилятор генерирует код, подобный следующему (псевдокод):

A a;
A::f (&a, 25, "");

Другими словами, компилятор работает со скрытым указателем this на каждую нестатическую функцию-член, и каждый вызов получает ссылку на экземпляр, к которому он был вызван. Это, в частности, означает, что вы можете вызывать нестатические функции-члены для указателей NULL.

A *a = NULL;
a->f (25, "");

Сбой происходит только тогда, когда вы фактически ссылаетесь на нестатические переменные-члены класса. Полученный отчет о сбое также иллюстрирует ответ на вопрос (1). Во многих случаях вы не будете аварийно завершать работу по адресу 0, но смещение этого. Это смещение соответствует смещению, которое переменная доступа имеет в структуре памяти, выбранной компилятором для class A (в этом случае многие компиляторы фактически смещают его с помощью 0x0, и class A в памяти не будет отличаться от struct A { int i; };). По сути, указатели на классы являются указателями на эквивалентную структуру C. Функции-члены реализованы как свободные функции, принимающие ссылку на экземпляр в качестве первого аргумента. Все и любые проверки доступа в отношении открытых, защищенных и закрытых членов выполняются компилятором заранее, сгенерированная сборка не имеет ни малейшего представления о какой-либо из этих концепций. Фактически, ранние версии C ++, как говорят, были наборами умных макросов C.

Структура памяти (как правило) немного меняется при наличии виртуальных функций.

...