«Несовместимый тип указателя» ПРЕДУПРЕЖДЕНИЕ ПРЕДУПРЕЖДЕНИЯ для 4-го аргумента QSORT
Вопрос
Я пытаюсь использовать стандартную библиотеку 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};
Вы не можете отличить указатель функции на другой тип, ваше текущее решение так же хорошо, он получает