Первый пример кода, вероятно, будет работать практически с любым компилятором и процессором; однако это технически неопределенное поведение, если вы следуете стандарту 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*
) или наоборот. Стандарт позволяет указателям на функции и указателям данных иметь разные размеры. Даже если он работает на вашем компьютере, он не всегда будет работать.
Не соответствует ли стандарт, что 'char *' и 'void *' имеют одинаковое представление? Я согласен, что все еще UB делает вызов с неправильным типом указателя функции, но я не верю, что представление аргументов является проблемой. Скорее проблема заключается в том, что патологическая реализация может использовать другое соглашение вызова для передачи 'void *' versus 'char *', например. имеющих отдельный набор регистров аргументов для каждого типа. –
@R.: Да, стандарт гарантирует, что 'char *' и 'void *' имеют одинаковые требования к представлению и выравниванию (C99 §6.2.5/27), но 'char **' и 'void *' не обязательно совместимы. –