Как qsort массив указателей на char в C?
Предположим у меня есть массив указателей на char в 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
в этой функции? Я ничего не понимаю. Почему двойной указатель? Почему моя первоначальная функция не сработала?
Спасибо, Бодя Cydo.
7 ответов
если это помогает держать вещи прямо в голове, тип, который вы должны бросить указатели в вашем компараторе такой же, как исходный тип указателя данных вы передаете в 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**
.
представьте себе, что ваши данные были double data[5]
.
ваш метод сравнения получит указатели (double*, переданные как void*) на элементы (double).
Теперь замените double на char * снова.
qsort
является достаточно общим для сортировки массивов, состоящих из других вещей, чем указатели. Вот почему параметр size существует. Он не может напрямую передавать элементы массива в функцию сравнения, поскольку во время компиляции не знает, насколько они велики. Поэтому он передает указатели. В вашем случае вы получаете указатели char *
, char **
.
функция сравнения принимает указатели на тип объекта в массиве, для сортировки. Поскольку массив содержит char *
ваша функция сравнения принимает указатели на char *
, иначе char **
.
С 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 *data[5] = { "boda", "cydo", "washington", "dc", "obama" };
- это оператор, запрашивающий у компилятора массив размером 5 символьных указателей. Вы инициализировали эти указатели на строковые литералы, но для компилятора это все еще массив из пяти указателей.
когда вы передаете этот массив в qsort
, массив указателей распадается на указатель, указывающий на первый элемент, в соответствии с правилами передачи параметров массива C.
поэтому вы должны обработать один уровень косвенности перед вами можно добраться до фактических массивов символов, содержащих константы.
@bodacydo вот программа, которая может объяснить, что другие программисты пытаются передать, но это будет в контексте "целых чисел"
#include <stdio.h>
int main()
{
int i , j;
int *x[2] = {&i, &j};
i = 10; j = 20;
printf("in main() address of i = %p, address of j = %p \r\n", &i, &j);
fun(x);
fun(x + 1);
return 0;
}
void fun(int **ptr)
{
printf("value(it would be an address) of decayed element received = %p, double dereferenced value is %d \r\n",*ptr, **ptr);
printf("the decayed value can also be printed as *(int **)ptr = %p \r\n", *(int **)ptr );
}