Может ли кто-нибудь объяснить это определение структуры dirent в Solaris?

Недавно я смотрел на структуру dirent (в dirent.h) и был немного озадачен ее определением.

ПРИМЕЧАНИЕ. Этот заголовочный файл взят с компьютера Solaris в моей школе.


typedef struct dirent {
    ino_t       d_ino;
    off_t       d_off;
    unsigned short  d_reclen;
    char        d_name[1];
} dirent_t;

В частности, поле d_name. Как это будет работать в операционной системе? Если вам нужно сохранить строку с завершающим нулем, что хорошего в массиве из одного символа? Я знаю, что вы можете получить адрес массива по его первому элементу, но я все еще запутался. Очевидно, что-то происходит, но я не знаю что. В моей домашней системе Fedora Linux это поле просто определяется как:

char d_name[256];

Теперь это имеет гораздо больший смысл по очевидным причинам. Может кто-нибудь объяснить, почему заголовочный файл Solaris определяет структуру именно так?


person Mr. Shickadance    schedule 18.02.2009    source источник


Ответы (4)


Как указывали другие, последний член структуры не имеет установленного размера. Однако длина массива, по мнению реализации, должна быть достаточной для размещения символов, которые она хочет в него поместить. Он делает это путем динамического выделения памяти для структуры, например, с malloc.

Однако удобно объявить член размером 1, потому что легко определить, сколько памяти занято любой dirent переменной d:

sizeof(dirent) + strlen(d.d_name)

Использование размера 1 также препятствует тому, чтобы получатель таких значений структуры пытался сохранить в нем свои собственные имена вместо того, чтобы выделять свои собственные dirent значения. Используя определение Linux, разумно предположить, что любое имеющееся у вас значение dirent будет принимать строку из 255 символов, но Solaris не дает никаких гарантий, что в его значениях dirent будет храниться больше символов, чем необходимо.

Я думаю, что именно C 99 ввел особый случай для последнего члена структуры. Вместо этого структуру можно было бы объявить так:

typedef struct dirent {
  ino_t d_ino;
  off_t d_off;
  unsigned short d_reclen;
  char d_name[];
} dirent_t;

У массива нет заявленного размера. Это известно как гибкий элемент массива. Он выполняет то же самое, что и версия Solaris, за исключением того, что нет иллюзий, что структура сама по себе может содержать любое имя. Глядя на это, вы понимаете, что это еще не все.

Используя "гибкое" объявление, объем занимаемой памяти можно отрегулировать следующим образом:

sizeof(dirent) + strlen(d.d_name) + 1

Это потому, что гибкий член массива не влияет на размер структуры.

Причина, по которой вы не видите подобные гибкие объявления чаще, особенно в коде библиотеки ОС, скорее всего, связана с совместимостью со старыми компиляторами, которые не поддерживают эту возможность. Это также для совместимости с кодом, написанным для нацеливания на текущее определение, которое сломается, если размер структуры изменится таким образом.

person Rob Kennedy    schedule 18.02.2009
comment
Фактически, запись d_reclen содержит реальный размер этого экземпляра структуры, вам не нужно вычислять его самостоятельно. - person raimue; 19.02.2009
comment
Ах ты прав. Тем не менее, тому, кто распределяет структуру, все еще нужно выяснить, сколько выделить, так что по-прежнему приятно иметь простой способ его вычислить. - person Rob Kennedy; 19.02.2009
comment
Я вернулся к этой проблеме, когда реализовал свой собственный драйвер ext2 для моей ОС для хобби. Ключевым моментом для меня является то, как я использую эту структуру. Я читаю записи каталога с диска, затем преобразую буфер в dirent_t. Тогда гибкий член массива соответствует символам имени файла. Затем просто прочтите name_len символа, начиная с d.name, и все. На практике это действительно неплохо работает. Возможно, вам нужно понять, как используется rec_len, чтобы понять, почему это имеет смысл. - person Mr. Shickadance; 29.09.2011

За структурой dirent в памяти сразу же следует блок памяти, содержащий остальную часть имени, и эта память доступна через поле d_name.

person Rob K    schedule 18.02.2009

Это шаблон, используемый в C для обозначения массива произвольной длины в конце структуры. Массивы в C не имеют встроенной проверки границ, поэтому, когда ваш код пытается получить доступ к строке, начинающейся с d_name, он продолжится после конца структуры. Это зависит от того, что readdir() выделит достаточно памяти для хранения всей строки плюс завершающий нуль.

person Commodore Jaeger    schedule 18.02.2009
comment
Почему бы просто не использовать указатель в структуре? Чтобы сэкономить несколько байтов? Я полагаю, что на уровне ОС это может быть так. - person Mr. Shickadance; 19.02.2009
comment
Указатель не делает то же самое. Чтобы использовать указатель, потребуется несколько выделений памяти - одно для структуры dirent и одно для имени, причем указатель указывает на имя. Использование шаблона однобайтового массива означает одно единственное выделение, в котором d_name является первым байтом имени. - person Andrew; 19.02.2009
comment
Вас понял. Спасибо за разъяснение, я не рассматривал необходимость дополнительных выделений. - person Mr. Shickadance; 19.02.2009

Мне это кажется микрооптимизацией. Имена обычно короткие, так зачем выделять пространство, которое, как вы знаете, останется неиспользованным. Кроме того, Solaris может поддерживать имена длиной более 255 символов. Чтобы использовать такую ​​структуру, вы просто выделяете необходимое пространство и игнорируете предполагаемый размер массива.

person dwc    schedule 18.02.2009