Почему такая структура содержит два поля массива, содержащие только один элемент?

Обратите внимание: этот вопрос не является дубликатом (одного массива элементов в структуре)

Следующий код извлекается из источника ядра Linux (версия: 3.14)

struct files_struct
{
    atomic_t count;
    struct fdtable __rcu *fdt;
    struct fdtable fdtab;

    spinlock_t file_lock ____cacheline_aligned_in_smp;
    int next_fd;
    unsigned long close_on_exec_init[1];
    unsigned long open_fds_init[1];
    struct file __rcu * fd_array[NR_OPEN_DEFAULT];
};

Мне просто интересно, почему close_on_exec_init и open_fds_init определяются как массивы, содержащие один элемент, а не только как unsigned long close_on_exec_init; и unsigned long open_fds_init;.

Ответ 1

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

Поле close_on_exec_init предоставляет исходное хранилище для fdt->close_on_exec, когда выделяется files_struct. (См. dup_fd в fs/file.c.)

Каждый бит fdt->close_on_exec устанавливается, если в соответствующем файловом дескрипторе установлен флаг "close-on-exec". Таким образом, Linux нужно выделить дополнительное пространство для fdt->close_on_exec, если процесс имеет более открытые файловые дескрипторы, чем количество бит в unsigned long.

Поле open_fds_init выполняет ту же функцию для поля fdt->open_fds. Поле fd_array выполняет ту же функцию для поля fdt->fd. (Обратите внимание, что fd_array имеет размер BITS_PER_LONG.)

Поля close_on_exec_init и open_fds_init ранее имели тип struct embedded_fd_set, но были изменены на голые массивы в this commit. Сообщение фиксации не объясняет, почему автор решил использовать одноэлементные массивы вместо голых скаляров. Возможно, автор (Дэвид Хауэллс) просто хотел избежать использования оператора &.

Ответ 2

Мое лучшее предположение: адреса этих полей используются гораздо чаще, чем их фактические значения. В этом случае создание массивов размера 1 позволяет печатать & каждый раз, когда их адрес необходим, поскольку в C, использующем имя массива в выражении, почти во всех случаях точно эквивалентно адресу адреса его первого элемента:

int x;
int y[1];

function_that_needs_address_of_int(&x);
function_that_needs_address_of_int(y);
function_that_needs_address_of_int(&y[0]);    // Identical to previous line

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

[EDIT: Как указано пользователем 3477950, имя массива не всегда совпадает с адресом его первого элемента - в определенных контекстах, например с аргументом sizeof, они означают разные вещи. (Единственный контекст, который я могу представить для C; в С++, передав имя массива в качестве аргумента, также может включить тип параметра шаблона, чтобы быть ссылочным типом.) ]