2009-07-02 3 views
4

это только я или этот код в Programming Pearls не так (quicksort хочет 2 const voids, нет?) Если это так, то мое решение правильно? Извинения, просто обучение ...Ошибка в функции qsort в программировании жемчуга?

int wordncmp(char *p, char* q) 
{ int n = k; 
    for (; *p == *q; p++, q++) 
     if (*p == 0 && --n == 0) 
      return 0; 
    return *p - *q; 
} 

int sortcmp(char **p, char **q) 
{ return wordncmp(*p, *q); 
} 
... 

qsort(word, nword, sizeof(word[0]), sortcmp); 

Это решение?

int sortcmp(const void *p, const void *q) 
{ return wordncmp(* (char * const *) p, * (char * const *) q); 
} 

ответ

7

Первый пример кода, вероятно, будет работать практически с любым компилятором и процессором; однако это технически неопределенное поведение, если вы следуете стандарту C на письмо.

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

В стандарте C указано, что вы можете использовать указатели на функции для других указателей функций с разными типами, но вы не можете разыгрывать и вызывать указатель функции casted. Однако, если вы повторно вернете указатель функции обратно к исходному типу, то вызов, который определил поведение, вызывает функцию оригинала.

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

Однако, поскольку практически на всех архитектурах, char ** и const void* представлены одинаково, вызов функции будет в значительной степени работать.

Если вы хотите получить определенное поведение, вы должны убедиться, что ваша функция компаратора имеет соответствующую подпись типа, а затем вы можете применить аргументы к соответствующему типу. Ваше решение точно соответствует действительности и не нарушает стандарт C. Хорошо сделано по const -корректность - многие люди не совсем понимают, что означает char * const *.

Вы также должны сделать wordncmp() принять параметры const char*, так как вы не изменяете параметры.

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

+0

Не соответствует ли стандарт, что 'char *' и 'void *' имеют одинаковое представление? Я согласен, что все еще UB делает вызов с неправильным типом указателя функции, но я не верю, что представление аргументов является проблемой. Скорее проблема заключается в том, что патологическая реализация может использовать другое соглашение вызова для передачи 'void *' versus 'char *', например. имеющих отдельный набор регистров аргументов для каждого типа. –

+0

@R.: Да, стандарт гарантирует, что 'char *' и 'void *' имеют одинаковые требования к представлению и выравниванию (C99 §6.2.5/27), но 'char **' и 'void *' не обязательно совместимы. –

2

Вы в верности, подпись для sortcmp не соответствует тому, что ожидает qsort. Ваша коррекция правильная. wordcmp также должен быть сделан const -корректно, поскольку вы технически теряете часть const -ness по пути.

int wordncmp(const char *p, const char* q)