Предупреждение компилятора «Несовместимый тип указателя» для 4-го аргумента qsort - PullRequest
4 голосов
/ 16 августа 2010

Я пытаюсь использовать стандартную библиотеку qsort для сортировки массива широких символов:

wchar_t a = L'a';
wchar_t a1 = L'ä';
wchar_t b = L'z';
wchar_t chararray[] = {b, a, a1};  
length = wcslen(chararray);

qsort(chararray, length, sizeof(wchar_t), wcscoll);

Теперь я думаю, что задействованные функции имеют следующие прототипы:

int wcscoll(const wchar_t *ws1, const wchar_t *ws2);
void qsort(void *base, size_t num, size_t size, int (*comp_func)(const void *, const void *))

Результаты полностью соответствуют ожидаемым, но почему я получаю предупреждение компилятора "passing argument 4 of ‘qsort’ from incompatible pointer type" ?И как я могу привести wcscoll, чтобы соответствовать прототипу?

Предупреждение исчезнет, ​​если я определю и передам отдельную функцию сравнения:

int widecharcomp(const void *arg1, const void *arg2)
{
    return wcscoll(arg1, arg2);
}

... но эта функция выглядит так, как будто она должна иметь обработку ошибок, когда аргументы невведите wchar_t *.

Ответы [ 4 ]

8 голосов
/ 16 августа 2010

Вы сделали в значительной степени правильный путь. В документации gcc для strcoll и wcscoll приведен пример, подобный этому, как правильный способ использования strcoll или wcscoll с qsort.

 /* This is the comparison function used with qsort. */

 int
 compare_elements (char **p1, char **p2)
 {
   return strcoll (*p1, *p2);
 }

 /* This is the entry point---the function to sort
    strings using the locale's collating sequence. */

 void
 sort_strings (char **array, int nstrings)
 {
   /* Sort temp_array by comparing the strings. */
   qsort (array, nstrings,
          sizeof (char *), compare_elements);
 }

Этот пример на самом деле вызывает предупреждение, от которого вы хотите избавиться, но опять же его можно обойти, изменив char** на const void* в аргументах на compare_elements, а затем явно приведя к const char**.

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

Тем не менее, в вашем коде есть ошибка . Функция сравнения получает не сравниваемые элементы, а указатели на сравниваемые элементы . Так что, если элементы являются строками, это означает указатель на указатель. Поэтому, когда вы пишете

return wcscoll(arg1, arg2);

Вы фактически передаете wscoll a wchar_t**, когда он ожидает wchar_t*. Правильный способ сделать это, подавив предупреждение, будет:

int widecharcomp(const void *arg1, const void *arg2)
{
    return wcscoll(*(const w_char_t**)arg1, *(const w_char_t**)arg2);
}

такой же уродливый, как и он.

Edit:

Просто еще раз взглянул на верхнюю часть вашего кода. Ваша ошибка здесь действительно двойная. Вы пытаетесь использовать wcscoll для сортировки символов. Это функция, предназначенная для сортировки строк (которые в C являются указателями на последовательности символов с нулем в конце). Выше написано, если вы пытаетесь отсортировать строки. Если вы хотите отсортировать символы, то wcscoll не подходит для использования, но все вышеизложенное в отношении qsort по-прежнему применимо.

4 голосов
/ 16 августа 2010

Есть две проблемы: вы перепутали wchar_t и wchar_t* и пытались выдать wchar_t* как void*.

Сначала вы сказали qsort отсортировать массив wchar_t. Но wcscoll не сравнивает wchar_t, он сравнивает строки широких символов, которые имеют тип wchar_t*. Тот факт, что ваше сравнение, похоже, сработало, связан с вашими данными испытаний, которые, как оказалось, хорошо работают в обеих интерпретациях.

Если вы хотите отсортировать символы, вам нужно вызвать соответствующую функцию (я недостаточно знаю API широких символов, чтобы сказать вам, какая из них). Если вы хотите отсортировать строки, вам нужно выделить массив строк (типа wchar_t *).

Более того, даже если у вас был массив wchar_t*, вы не могли бы переносить wcscoll в качестве аргумента qsort. Проблема в том, что нет гарантии, что wchar_t* и void* имеют одинаковое представление. Некоторые машины имеют указатели слов, которые имеют представление, отличное от указателей байтов; на такой машине qsort будет передавать байтовые указатели на элементы массива на wcscoll, и это не сработает, потому что wcscoll ожидает байтовые указатели. Решение состоит в том, чтобы написать тривиальную функцию-обертку, которая при необходимости выполняет преобразование. Тривиальная обертка часто необходима с qsort.

2 голосов
/ 16 августа 2010

Вы уже закодировали свое решение (однако, смотрите другие ответы и правки в конце этого о выборе используемой функции сравнения и данных, передаваемых в qsort()).

Вы можете отбросить функцию-обертку, приведя указатель функции к qsort() к соответствующему типу, но я думаю, что использование обертки - лучшее решение с точки зрения обслуживания. Если вы действительно хотите избежать использования функции-обертки (может быть, вы столкнулись с измеримой проблемой с перфорированием), вы можете разыграть следующее:

qsort(chararray, length, sizeof(wchar_t), (int(*)(const void*,const void*))wcscoll);

Или сделайте его, возможно, более читабельным, используя typedef для типа функции сравнения:

typedef
int (*comp_func_t)(const void *, const void *);

/* ... */
qsort(chararray, length, sizeof(wchar_t), (comp_func_t) wcscoll);

К сожалению, прямой C qsort() не может быть безопасным с точки зрения типов, поэтому у него не может быть "обработки ошибок, когда аргументы не имеют типа wchar_t". Вы, программист, несете ответственность за обеспечение передачи правильных данных, размеров и функции сравнения на qsort().


Edit:

Чтобы решить некоторые из проблем, упомянутых в других ответах о типах, передаваемых в функцию сравнения, вот процедура, которая может использоваться для сортировки wchar_t с использованием последовательности сортировки текущей локали. В библиотеке может быть что-то лучше, но я не знаю об этом сейчас:

int wchar_t_coll( const void* p1, const void* p2)
{
    wchar_t s1[2] = {0};
    wchar_t s2[2] = {0};

    s1[0] = * (wchar_t*)p1;
    s2[0] = * (wchar_t*)p2;

    return wcscoll( s1, s2);
}

Также обратите внимание, что chararray, который вы передаете wcslen(), не завершен должным образом - вам понадобится 0 в конце инициализатора:

wchar_t chararray[] = {b, a, a1, 0};  
0 голосов
/ 16 августа 2010

Вы не можете привести указатель функции к другому типу, ваше текущее решение так же хорошо, как оно получается

...