Платой за это является дополнительная память для размещения указателей и модификация функций сравнения для дополнительного перенаправления указателей при сравнении структур. Полученной выгодой может стать значительное ускорение работы, поскольку на каждом шаге перемещается лишь четырех- или восьмибайтный указатель вместо большой структуры. (Наша
struct employee
имеет размер по крайней мере 68 байтов. При обмене четырехбайтных указателей перемещается в 17 раз меньше данных, чем при обмене структур.) Для тысяч размещенных в памяти структур разница мажет быть существенной.
ЗАМЕЧАНИЕ. Если вы являетесь программистом С++, знайте!
qsort
может быть опасной для использования с массивами объектов!
qsort
осуществляет простые перемещения памяти, копируя байты. Она совершенно ничего не знает о конструкциях С++, таких, как конструкторы копирования или функции
operator=
. Вместо этого используйте одну из функций сортировки STL [67] или используйте методику отдельного массива указателей.
В разделе 5.3 «Чтение каталогов» мы продемонстрировали, как элементы каталогов возвращаются в физическом порядке каталога. В большинстве случаев гораздо полезнее иметь содержимое каталога отсортированным каким-нибудь образом, например, по имени или по времени изменения. Хотя и не стандартизованные POSIX, несколько процедур упрощают это, используя
qsort
в качестве лежащего в основе сортирующего агента:
#include <dirent.h> /* Обычный */
int scandir(const char *dir, struct dirent ***namelist,
int (*select)(const struct dirent*),
int (*compare)(const struct dirent **, const struct dirent **));
int alphasort(const void *a, const void *b);
int versionsort(const void *a, const void *b); /* GLIBC */
Функции
scandir
и
alphasort
были сделаны доступными в 4.2 BSD и широко поддерживаются [68] ,
versionsort
является расширением GNU.
68
Заметным исключением является лишь Sun Solaris, где эти две функции существуют лишь в трудной для использования библиотеке совместимости с BSD — Примеч. автора.
scandir
читает каталог, имя которого дано в
dir
, создает с использованием
malloc
массив указателей
struct dirent
и устанавливает
*namelist
, чтобы он указывал на начало этого массива. Как массив указателей, так и указываемые структуры
struct dirent
выделяются с помощью
malloc
; вызывающий код должен использовать
free
, чтобы избежать утечек памяти.
Для выбора нужных элементов используйте указатель функции
select
. Когда это значение равно
NULL
, все действительные элементы каталога включаются в конечный массив. В противном случае
(*select)
вызывается для каждого элемента, и те элементы, для
которых она возвращает ненулевое (истинное) значение, включаются в массив.
Указатель функции compare сравнивает два элемента каталога. Он передается функции
qsort
для использования при сортировке.
alphasort
лексикографически сравнивает имена файлов. Она использует для сравнения функцию
strcoll
.
strcoll
похожа на
strcmp
, но учитывает связанные с местной спецификой правила сортировки (см. раздел 13.4 «Не могли бы вы написать это для меня по буквам?»).
versionsort
является расширением GNU, которое использует для сравнения имен файлов функцию GNU
strverscmp
(см. strverscmp(3).) Короче говоря, эта функция понимает обычные соглашения по версиям имен файлов и сравнивает их соответствующим образом.