Распределение памяти члена структуры C++

У меня есть структура, которая выглядит так:

struct rtok {
    char type;
    std::string val;
    bool term;
};

Я пишу простой интерпретатор, и эта структура "rtok" - это то, как я представляю токен. У меня есть вектор "rtoks", который я перебираю для создания дерева разбора.

Мой вопрос заключается в том, что если в моей структуре 3 члена и я даю значение только 1 члену, другие члены все равно будут занимать память?

Я имею в виду, что если я установлю "val" равным "test", мой токен займет всего 4 байта или 6 байтов? (4 байта для "val", 1 байт для типа, 1 байт для термина)

4 ответа

Решение

Если у вас нет дополнительных членов или виртуальных функций, ваша структура всегда будет занимать sizeof(char) + sizeof(string) + sizeof(bool) + possible padding, string часть выделяет себе кусок памяти, который она освобождает при уничтожении. Однако эта память технически не является частью той, которая была выделена для struct,

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

Не волнуйтесь, это займет значительно больше, чем вы думаете.

Есть два фактора: выравнивание данных и реализация внутреннего типа. Во-первых, о выравнивании данных: все типы в вашей структуре естественно выровнены, что означает, что char можно по любому адресу, но void* может потребоваться выравнивание 4 или 8 в зависимости от архитектуры.

Итак, если мы предполагаем, что std::string использует просто char* внутренне, чтобы держать вас в строке макет на x32 будет:

struct rtok {
  char type;
  char* val; // here char * for simplicity
  bool term;
};

sizeof(rtok) оператор выдаст 12 байтов, а не 6, и объем памяти будет выглядеть следующим образом:

00: type (one byte)
01: padding
02: padding
03: padding
04-07: char * (4 bytes)
08: term (one byte)
09-0a: padding (3 bytes)

Теперь, если мы заменим char* с std::stringмы бы обнаружили, что размер структуры вырос, так как sizeof(std::string) обычно больше, чем 4 байта.

НО, мы не вычислили само строковое значение... И здесь мы попадаем в область управления и распределения кучи.

Память для хранения значения выделяется в куче, и код обычно запрашивает столько, сколько ему нужно, поэтому для строки из 10 символов это будет 11 байтов (10 символов плюс 1 байт для нулевого терминатора).

И куча имеет собственную сложную структуру с кучей небольших блоков и т. Д. На практике это означает, что минимальное потребляемое количество составляет около 16 байтов или более. Эта сумма не то, что вы можете использовать, эта сумма предназначена для управления внутренними структурами кучи, и единственная полезная сумма может быть всего 1 байт.

Если вы сложите все, вы обнаружите, что даже если вы планируете использовать только два символа плюс тип, объем потребляемой памяти будет намного-намного больше.

Данный тип struct всегда имеет одинаковый размер. Это гарантия от Стандарта. Когда вы определяете struct, вы говорите: "У меня есть объект такого размера (сумма размеров элементов + возможное заполнение для выравнивания для каждого элемента), и они должны быть в этом порядке в памяти (такой же порядок определений элементов содержится в struct определение)":

(N4296) 9,2
/ 12 [ Пример: простой пример определения класса

struct tnode {
  char tword[20];
  int count;
  tnode* left;
  tnode* right;
};

который содержит массив из двадцати символов, целое число и два указателя на объекты одного типа. [...] - конец примера

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

Обратите внимание на спецификатор "с тем же контролем доступа". Если ваша структура имеет комбинацию элементов данных с различными спецификаторами доступа, компоновка может отличаться от ожидаемой, кроме гарантии, которая дает что-то вроде:

public:
   some_type public_1;
private:
   some_type private_1;
public:
   some_type public_2;

public_2 будет по более высокому адресу, чем public_1, Помимо этого - не указано. private_1 может быть по более низкому или более высокому адресу.

По поводу вашего другого вопроса (задается в комментариях):

Было бы лучше использовать класс вместо структуры тогда?

В C++ struct а также class по существу одинаковы, единственное отличие состоит в том, что члены (и наследование) struct являются public по умолчанию, тогда как с class они есть private по умолчанию. Это еще яснее в примечании и примере из Стандарта:

§3.1 Объявления и определения [basic.def]
/ 3 [ Примечание: В некоторых случаях реализации C++ неявно определяют конструктор по умолчанию (12.1), конструктор копирования (12.8), конструктор перемещения (12.8), оператор назначения копирования (12.8), оператор назначения перемещения (12.8) или деструктор (12.4) функции-члены. —Конец примечания ] [ Пример: дано

#include <string>
struct C {
    std::string s; // std::string is the standard library class (Clause 21)
};
int main() {
    C a;
    C b = a;
    b = a;
}

реализация будет неявно определять функции, чтобы сделать определение C эквивалентным

struct C {
    std::string s;
    C() : s() { }
    C(const C& x): s(x.s) { }
    C(C&& x): s(static_cast<std::string&&>(x.s)) { }
    // : s(std::move(x.s)) { }
    C& operator=(const C& x) { s = x.s; return *this; }
    C& operator=(C&& x) { s = static_cast<std::string&&>(x.s); return *this; }
    // { s = std::move(x.s); return *this; }
    ~C() { }
};

- конец примера ]

Обратите внимание, что в примере из стандарта используется struct а не class чтобы проиллюстрировать этот момент для не POD structs, Это еще более ясно, если учесть, что определение struct в стандарте есть в разделе 9 - "Классы".

Как сказано ранее, struct всегда фиксированного размера. Есть несколько способов преодолеть это ограничение:

  1. Сохраните указатель и выделите для него память кучи.
  2. Использовать "несвязанный" массив char[1] в качестве последнего члена, и выделить память для struct сам в кучу.
  3. использование union чтобы сэкономить место для перекрывающихся членов.
Другие вопросы по тегам