Подтвердить что ты не робот

Компоновка памяти объекты С++

Мне в основном интересно, как С++ выдает объект в памяти. Итак, я слышал, что динамические трансляции просто настраивают указатель объекта в памяти со смещением; и переинтерпретировать вид позволяет нам делать что-либо с этим указателем. Я этого не понимаю. Детали будут оценены!

4b9b3361

Ответ 1

Каждый класс излагает свои члены данных в порядке объявления.
Компилятору разрешено размещать отступы между членами, чтобы сделать доступ эффективным (но он не разрешен для повторного заказа).

Как работает dynamic_cast<> - это деталь реализации компилятора и не определяется стандартом. Все это будет зависеть от ABI, используемого компилятором.

reinterpret_cast<> работает, просто изменяя тип объекта. Единственное, что вы можете гарантировать, что работает, - это то, что наведение указателя на void * и обратно на тот же указатель на класс даст вам тот же указатель.

Ответ 2

Макет памяти в основном оставлен для реализации. Ключевым исключением является то, что переменные-члены для данного спецификатора доступа будут в порядке их объявления.

§ 9.2.14

Нестатические члены данных (неединичного) класса с одинаковым доступом (раздел 11) распределяются так, что более поздние члены имеют более высокий адресов внутри объекта класса. Порядок распределения нестатических члены данных с различным контролем доступа не определены (11). Требования к выравниванию реализации могут приводить к двум соседним членам не выделяться сразу после друг друга; так что требования к пространству для управления виртуальными функциями (10.3) и виртуальные базовые классы (10.1).

Помимо переменных-членов, классу или структуре необходимо предоставить пространство для переменных-членов, подобъектов базовых классов, управления виртуальными функциями (например, виртуальную таблицу) и заполнения и выравнивания этих данных. Это зависит от реализации, но спецификация Itanium ABI является популярным выбором. gcc и clang придерживаются (по крайней мере, до степени).

http://mentorembedded.github.io/cxx-abi/abi.html#layout

Itanium ABI, конечно же, не является частью стандарта С++ и не является обязательным. Чтобы получить более подробную информацию, вам нужно обратиться к документации и инструментам разработчика. clang предоставляет инструмент для просмотра макета памяти классов. В качестве примера:

class VBase {
    virtual void corge();
    int j;
};

class SBase1 {
    virtual void grault();
    int k;
};

class SBase2 {
    virtual void grault();
    int k;
};

class SBase3 {
    void grault();
    int k;
};

class Class : public SBase1, SBase2, SBase3, virtual VBase {
public:
    void bar();
    virtual void baz();
    // virtual member function templates not allowed, thinking about memory
    // layout and vtables will tell you why
    // template<typename T>
    // virtual void quux();
private:
    int i;
    char c;
public:
    float f;
private:
    double d;
public:
    short s;
};

class Derived : public Class {
    virtual void qux();
};

int main() {
    return sizeof(Derived);
}

После создания исходного файла, использующего макет памяти класса, clang покажет макет памяти.

$ clang -cc1 -fdump-record-layouts layout.cpp

Макет для Class:

*** Dumping AST Record Layout
   0 | class Class
   0 |   class SBase1 (primary base)
   0 |     (SBase1 vtable pointer)
   8 |     int k
  16 |   class SBase2 (base)
  16 |     (SBase2 vtable pointer)
  24 |     int k
  28 |   class SBase3 (base)
  28 |     int k
  32 |   int i
  36 |   char c
  40 |   float f
  48 |   double d
  56 |   short s
  64 |   class VBase (virtual base)
  64 |     (VBase vtable pointer)
  72 |     int j
     | [sizeof=80, dsize=76, align=8
     |  nvsize=58, nvalign=8]

Подробнее об этой функции clang можно найти в блоге Eli Bendersky:

http://eli.thegreenplace.net/2012/12/17/dumping-a-c-objects-memory-layout-with-clang/

gcc предоставляет аналогичный инструмент, `-fdump-class-hierarchy '. Для класса, указанного выше, он печатает (между прочим):

Class Class
   size=80 align=8
   base size=58 base align=8
Class (0x0x141f81280) 0
    vptridx=0u vptr=((& Class::_ZTV5Class) + 24u)
  SBase1 (0x0x141f78840) 0
      primary-for Class (0x0x141f81280)
  SBase2 (0x0x141f788a0) 16
      vptr=((& Class::_ZTV5Class) + 56u)
  SBase3 (0x0x141f78900) 28
  VBase (0x0x141f78960) 64 virtual
      vptridx=8u vbaseoffset=-24 vptr=((& Class::_ZTV5Class) + 88u)

Он не перечисляет переменные-члены (или, по крайней мере, я не знаю, как их получить), но вы можете сказать, что они должны быть между смещением 28 и 64, точно так же, как в макете clang.

Вы можете видеть, что один базовый класс выделяется как primary. Это устраняет необходимость в настройке указателя this, когда к Class обращается как SBase1.

Эквивалент для gcc:

$ g++ -fdump-class-hierarchy -c layout.cpp

Эквивалент для Visual С++:

cl main.cpp /c /d1reportSingleClassLayoutTest_A

см. https://blogs.msdn.microsoft.com/vcblog/2007/05/17/diagnosing-hidden-odr-violations-in-visual-c-and-fixing-lnk2022/

Ответ 3

Ответ: "Это сложно". Динамический бросок не просто настраивает указатели со смещением; он может фактически получить внутренние указатели внутри объекта, чтобы выполнить свою работу. GCC следует за ABI, разработанным для Itanium, но реализуется более широко. Здесь вы можете найти подробные сведения: Itanium С++ ABI.

Ответ 4

Как указывалось ранее, полная информация сложна, болезненна для чтения и действительно полезна только разработчикам компилятора и варьируется между компиляторами. В принципе, каждый объект содержит следующее (обычно в этом порядке):

  • Информация о времени выполнения
  • Не виртуальные базовые объекты и их данные (возможно, в порядке объявления).
  • Переменные участника
  • Виртуальные базовые объекты и их данные (возможно, в порядке поиска дерева DFS).

Эти фрагменты данных могут быть или не могут быть дополнены, чтобы упростить выравнивание памяти и т.д. Скрытые в информации о времени выполнения данные относятся к типу, v-таблицам для виртуальных родительских классов и т.д., все из которых являются специфичными для компилятора.

Когда дело касается кастингов, reinterpret_cast просто изменяет тип данных С++ указателя и ничего не делает, поэтому вам лучше быть уверенным, что вы знаете, что вы делаете, когда используете его, иначе вы несете беспорядок идет плохо. dynamic_cast делает то же самое, что и static_cast (при изменении указателя), за исключением того, что использует информацию типа времени выполнения, чтобы выяснить, может ли она быть применена к данному типу и как это сделать. Опять же, все это специфично для компилятора. Обратите внимание, что вы не можете dynamic_cast a void*, потому что ему нужно знать, где найти информацию типа времени выполнения, чтобы он мог выполнять все свои замечательные проверки времени выполнения.

Ответ 5

на этот вопрос уже ответил http://dieharddeveloper.blogspot.in/2013/07/c-memory-layout-and-process-image.html вот отрывок оттуда: В середине адресного пространства процесса для общих объектов сохраняется область. Когда создается новый процесс, диспетчер процессов сначала отображает два сегмента из исполняемого файла в память. Затем он декодирует заголовок ELF программы. Если заголовок программы указывает, что исполняемый файл был связан с общей библиотекой, диспетчер процессов (PM) будет извлекать имя динамического интерпретатора из заголовка программы. Динамический интерпретатор указывает на общую библиотеку, содержащую код компоновщика времени выполнения.