2010-08-16 6 views
4

Я пытаюсь использовать стандартные библиотеки qsort для сортировки массива широких символов:предупреждения компилятора «Несовместимый указатель типа» для 4-го аргумента 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

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

+0

Это очень сжатое объяснение. Большое спасибо. Я собираюсь отметить это правильно, поскольку это, вероятно, будет более полезно для будущих читателей, хотя я лично узнал больше от Тайлера Мак-Хенри, который подробно обсуждает код. Поэтому будущие читатели отмечают: оба стоят вашего времени, если вы столкнулись с той же проблемой. – chryss

8

Вы сделали очень правильный путь.The gcc documentation for strcoll and 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. 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); 
} 

уродлив, как это.

Edit:

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

+0

Хорошо поймать ошибку, однако, похоже, что OP хочет сортировать символы внутри строки, и в этом случае он получает правильные указатели на отсортированный элемент, но 'wcscoll()' не является правильным функцию, вызываемую, если символы не перемещаются в локальную, одну длину символа, строки с нулевым завершением (в этом случае функция обертки является довольно обязательной). Существует ли стандартная функция lib для сравнения значений одного символа/wchar_t с использованием последовательности сортировки локали? –

+0

Да, я (она, а не он, кстати) хотела начать сортировку отдельных символов перед сортировкой целых строк. Я пытаюсь получить доступ к wchar_t. – chryss

+0

Большое спасибо. Я все еще работаю над перевариванием этого, очень полезно. Первоначальная ошибка состояла в том, чтобы думать, что wcscoll сортирует символы. – chryss

0

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

2

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

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

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

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

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}; 
+1

Привет, Майкл; вы написали: «Вы можете отбросить функцию-обертку, передав указатель функции, который вы передаете qsort(), соответствующему типу». Это настоятельно не рекомендуется в многочисленных часто задаваемых вопросах C, как правило, в вопросе «Я пытаюсь передать функцию« strcmp »на« qsort ». Этот ответ всегда должен использовать обертку. Обоснованием, вероятно, является то, что ABI может передавать аргументы по-разному в соответствии с тонкими различиями в своих типах. Вы можете отбрасывать тип указателя функции на другой и отбрасывать исходный тип, но это единственное законное использование функций-указателей-указателей. –

+0

@ Паскаль: Я согласен. Я должен укрепить комментарий моего ответа к этому вопросу. –