2010-08-15 4 views
17

Предположим, что у меня есть массив указателей на Char в C:Как выполнить qsort массив указателей на 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.

+2

'data' должны быть объявлены' const'. –

+0

Билли, если он const, он все еще может быть отсортирован? – bodacydo

+1

Да. Массив может быть не 'const', но указатели, содержащиеся в этом массиве, должны быть' const'. Вам не разрешено изменять константные литералы времени компиляции (это неопределенное поведение для этого). Чтобы получить это, вам нужны данные 'const char * [5]'. Если вы хотите, чтобы массив тоже был постоянным, тогда вы будете использовать 'const char * const data [5]'. –

ответ

17

Если это помогает держать вещи прямо в голове, то тип, который вы должны наложить указатели на ваш компаратор, совпадает с исходным типом указателя данных, который вы передаете в qsort (что qsort docs вызывает base). Но для qsort, чтобы быть общим, он просто обрабатывает все как void*, независимо от того, что он «на самом деле».

Итак, если вы сортируете массив из целых чисел, вы пройдете в int* (преобразован в void*). qsort вернет вам два void* указателей на компаратор, который вы конвертируете в int*, и разыгрываете, чтобы получить значения int, которые вы фактически сравниваете.

Теперь замените int с char*:

если вы сортировки массива char*, то вы пройдете в char** (преобразованные в void*). qsort вернет вам два void* указателей на компаратор, который вы конвертируете в char**, и разыгрываете, чтобы получить значения char*, которые вы на самом деле сравниваете.

В вашем примере, поскольку вы используете массив, char**, который вы передаете, является результатом массива char*, «разлагающегося» на указатель на его первый элемент. Так как первым элементом является char*, указателем на него является char**.

3

Предположите, ваши данные были double data[5].

Ваш метод сравнения получит указатели (double *, переданные как void *) на элементы (double).
Теперь замените double на char * снова.

2

qsort достаточно общий, чтобы сортировать массивы, состоящие из других вещей, кроме указателей. Вот почему параметр размера есть. Он не может передавать элементы массива непосредственно в функцию сравнения, поскольку во время компиляции он не знает, насколько они велики. Поэтому он передает указатели. В вашем случае вы получаете указатели на char *, char **.

+0

Не понимаю, извините. Первый аргумент 'qsort' - это' * '. Я передаю '**'. Это означает, что я эффективно передаю только один '*'. Но один '*' является именно символом 'char *'. Видеть? Вот почему я смущен. – bodacydo

+0

@bodacydo: Важным моментом является то, что функция сравнения принимает указатели на * элементы * массива. Поскольку каждый элемент вашего массива является указателем на символ, функция сравнения работает с указателями на указатели на символ. – jamesdlin

0

из 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 ** (то есть указатель на указатель на символ).

0

char *data[5] = { "boda", "cydo", "washington", "dc", "obama" };

является заявление с просьбой компилятор для массива размером 5 символьных указателей. Вы инициализировали эти указатели для строковых литералов, но для компилятора он по-прежнему представляет собой массив из пяти указателей.

Когда вы передаете этот массив в qsort, массив указателей распадается на указатель, указывающий на первый элемент, в соответствии с правилами передачи параметров массива C.

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

2

Функция сравнения принимает указатели на тип объекта, который находится в массиве, который вы хотите отсортировать. Поскольку массив содержит char *, ваша функция сравнения принимает указатели на char *, aka char **.

0

@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); 
}