дополнительное заполнение между структурами в C - PullRequest
0 голосов
/ 13 января 2020

У меня есть структура в C:

typedef struct Node {
    int data; // 4 bytes int + 4 bytes for alignment
    struct Node* prev; // 8 bytes pointer
    struct Node* next; // 8 bytes pointer
} Node;

Размер этой структуры составляет 24 байта (8 + 8 + 8). Когда я использую sizeof (Node), компилятор также показывает 24 байта.

Однако, когда я создаю две или более структур в куче (одна за другой) и смотрю на их расположение в памяти, там 8 байтов промежутки между каждой структурой узла. Например:

11121344 (the 1st Node address)
11121376 (the 2nd Node address) // 376-344 = 32-24 = 8 extra bytes
11121408 (the 3rd Node address) // 408-376 = 32-24 = 8 extra bytes

Можете ли вы объяснить, почему компилятор разделяет структуры узлов путем добавления 8 байтов между узлами?

Ответы [ 2 ]

3 голосов
/ 13 января 2020

Есть 2 возможных причины для вашего наблюдения:

  1. Стандарт C требует, чтобы malloc всегда возвращал фрагменты памяти с максимальным выравниванием, чтобы предотвратить проблемы выравнивания, независимо от того, для чего вы выделяете.
  2. malloc управляет частями памяти внутри с помощью некоторого вида структур данных. В зависимости от реализации, это добавит дополнительную информацию к каждому куску памяти для внутреннего использования. Например, mallo c может управлять частями памяти в связанном списке, тогда потребуется, чтобы каждый блок содержал дополнительный указатель, указывающий на следующий блок.

Максимальное выравнивание зависит от архитектуры и используемая реализация компилятора / malloc.

Для вашего случая и предполагается, что glib c взят прямо из документов glibc/malloc.c:

 Alignment:                              2 * sizeof(size_t) (default)
       (i.e., 8 byte alignment with 4byte size_t). This suffices for
       nearly all current machines and C compilers. However, you can
       define MALLOC_ALIGNMENT to be wider than this if necessary.
  Minimum overhead per allocated chunk:   4 or 8 bytes
       Each malloced chunk has a hidden word of overhead holding size
       and status information.
  Minimum allocated size: 4-byte ptrs:  16 bytes    (including 4 overhead)
              8-byte ptrs:  24/32 bytes (including, 4/8 overhead)

Таким образом malloc в вашем случае будет соответствовать 2 * sizeof(size_t) = 16 байт.

Также обратите внимание на упомянутое «скрытое использование». Эти служебные данные должны хранить дополнительную внутреннюю информацию, используемую для управления памятью ...

3 голосов
/ 13 января 2020

Можете ли вы объяснить, почему компилятор разделяет структуры узлов, добавляя 8 байтов между узлами?

Это совпадение. Не существует правил о том, как планировать память для любой последовательности вызовов malloc().

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

Если вам нужны фиксированные относительные адреса, используйте массив

struct Node arr[3];
ptrdiff_t delta10 = &arr[1] - &arr[0];
ptrdiff_t delta20 = &arr[2] - &arr[0];
ptrdiff_t delta21 = &arr[2] - &arr[1];
if (delta10 != delta21) /* cannot happen */;

или выделите группу элементов (возможно, с realloc()) одновременно

struct Node *elements = malloc(3 * sizeof *elements);
ptrdiff_t delta10 = &elements[1] - &elements[0];
ptrdiff_t delta20 = &elements[2] - &elements[0];
ptrdiff_t delta21 = &elements[2] - &elements[1];
if (delta10 != delta21) /* cannot happen */;
free(elements);
...