«Несовместимый тип указателя» ПРЕДУПРЕЖДЕНИЕ ПРЕДУПРЕЖДЕНИЯ для 4-го аргумента QSORT

StackOverflow https://stackoverflow.com/questions/3495564

Вопрос

Я пытаюсь использовать стандартную библиотеку 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 *.

Это было полезно?

Решение

Есть две проблемы: вы перепутали 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.

Другие советы

Вы сделали почти правильный путь. Документация 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**.

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

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

return wcscoll(arg1, arg2);

Вы на самом деле проезжаете wscoll а wchar_t** Когда он ожидает wchar_t*. Анкет Правильный способ сделать это, подавляя предупреждение, будет:

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

как уродливая как то есть.

Редактировать:

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

Вы уже закодировали свое решение (однако, см. Другие ответы и изменения в конце этого, с выбором функции сравнения, которую вы используете, и передача данных в 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() Не может быть Typesafe, поэтому у него не может быть «обработка ошибок, когда аргументы не имеют типа WCHAR_T». Вы, программист, несете ответственность за обеспечение того, чтобы вы передали правильные данные, размеры и функцию сравнения qsort().


Редактировать:

Чтобы решить некоторые из проблем, упомянутых в других ответах о типах, передаваемых функцией сравнения, вот подпрограмма, которую можно использовать для сортировки 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};  

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top