Предположим, что у меня есть массив указателей для обугливания в C:
char *data[5] = { "boda", "cydo", "washington", "dc", "obama" };
И я хочу отсортировать этот массив с помощью qsort:
qsort(data, 5, sizeof(char *), compare_function);
Я не могу придумать сравнить функцию. По некоторым причинам это не работает:
int compare_function(const void *name1, const void *name2)
{
const char *name1_ = (const char *)name1;
const char *name2_ = (const char *)name2;
return strcmp(name1_, name2_);
}
Я сделал большой поиск и нашел, что должен был использовать **
в qsort:
int compare_function(const void *name1, const void *name2)
{
const char *name1_ = *(const char **)name1;
const char *name2_ = *(const char **)name2;
return strcmp(name1_, name2_);
}
И это работает.
Может любой объяснять использование *(const char **)name1
в этой функции? Я не понимаю это вообще. Почему двойной указатель? Почему мой оригинал не функционировал работа?
Спасибо, Boda Cydo.
Если это поможет вам разобраться, то тип, к которому вы должны приводить указатели в компараторе, совпадает с исходным типом указателя данных, который вы передаете в qsort
(который в документации по qsort называется base
). Но чтобы qsort
был универсальным, он просто обрабатывает все как void*
, независимо от того, чем это "на самом деле" является.
Так, если вы сортируете массив ints, то вы передадите int*
(преобразованный в void*
). qsort вернет вам два void*
указателя на компаратор, которые вы преобразуете в int*
и разыменуете, чтобы получить int
, которые вы действительно сравниваете.
Теперь замените int
на char*
:
если вы сортируете массив из char*
, то вы передадите char**
(преобразованный в void*
). qsort вернет вам два void*
указателя на компаратор, которые вы преобразуете в char**
и разыменуете, чтобы получить char*
значения, которые вы действительно сравниваете.
В вашем примере, поскольку вы используете массив, char**
, который вы передаете, является результатом того, что массив char*
"распадается" до указателя на его первый элемент. Поскольку первый элемент - это char*
, указатель на него - это char**
.
Представьте, что ваши данные были двойными данными [5]
.
Ваш метод сравнения будет получать указатели (double *, переданные как void *) на элементы (double).
Теперь снова замените double на char *.
qsort
достаточно общий для сортировки массивов, состоящих не из указателей, а из других объектов. Поэтому и есть параметр размера. Он не может напрямую передавать элементы массива в функцию сравнения, так как во время компиляции не знает, насколько они велики. Следовательно, он передает указатели. В вашем случае вы получите указатели на char *
, char **
.
char * data [5] = {"boda", "cydo", "Washington", "dc", "obama"};
- это инструкция, запрашивающая у компилятора массив 5 символьных указателей. . Вы инициализировали эти указатели на строковые литералы, но для компилятора это все еще массив из пяти указателей.
Когда вы передаете этот массив в qsort
, массив указателей распадается на указатель, указывающий на первый элемент, в соответствии с правилами передачи параметров массива C.
Следовательно, вы должны обработать один уровень косвенного обращения, прежде чем сможете перейти к фактическим символьным массивам, содержащим константы.
от man qsort
:
The contents of the array are sorted in ascending
order according to a comparison function pointed to by
compar, which is called with two arguments that **point**
to the objects being compared.
Похоже, функция сравнения получает указатели на элементы массива. Теперь указатель на char *
является char **
(т.е. указатель на указатель на символ).
Функция сравнения принимает указатели на тип объекта, находящегося в массиве, который вы хотите отсортировать. Поскольку массив содержит char *
, ваша функция сравнения принимает указатели на char *
, он же char **
.