Функция Qsort Сравнения
Я новичок в C и я пытаюсь понять функцию сравнения, необходимые для функции qsort.
Часть Первая: Синтаксис
простое предлагаемое использование-это (я включил некоторый основной () код для печати результатов):
#include <stdio.h>
#include <stdlib.h>
int values[] = { 40, 10, 100, 90, 20, 25, 12, 13, 10, 40 };
int compare(const void *a, const void *b)
{
const int *ia = (const int *)a; // casting pointer types
const int *ib = (const int *)b;
return *ia - *ib;
}
int main()
{
int n;
for (n=0; n<10; n++)
{
printf("%d ",values[n]);
}
printf("n");
qsort(values, 10, sizeof(int), compare);
for (n=0; n<10; n++)
{
printf("%d ",values[n]);
}
printf("n");
system("pause");
return 0;
}
Я не понимаю, почему вам нужны все дополнительные вещи в функции сравнения, поэтому я упростил его до этого:
int compare (int *a, int *b)
{
return *a-*b;
}
Это все еще работает и производит те же результаты. Может кто-нибудь объяснить мне что я удалил, и почему он все еще работает?
Часть Вторая: Почему Указатели?
кроме того, мне действительно нужно использовать указатели? Почему я не могу просто сравнить "a" и " b " прямо так (это не работает):
int compare (int a, int b)
{
return a-b;
}
по какой-то причине, с многомерным массивом, я смог уйти от использования указателей, и по какой-то причине это сработало! Что происходит? (Пример кода сортировки многомерного массива по 2-му элементу в каждом sub array):
#include <stdio.h>
#include <stdlib.h>
int values[7][3] = { {40,55}, {10,52}, {100,8}, {90,90}, {20,91}, {25,24} };
int compare(int a[2], int b[2])
{
return a[1] - b[1];
}
int main()
{
int n;
for (n=0; n<6; n++)
{
printf("%d,",values[n][0]);
printf("%d ",values[n][1]);
}
printf("n");
qsort(values, 6, sizeof(int)*3, compare);
for (n=0; n<6; n++)
{
printf("%d,",values[n][0]);
printf("%d ",values[n][1]);
}
printf("n");
system("pause");
return 0;
}
Я очень рад, что многомерная сортировка массивов работает, так как это мой цель в любом случае, но я понятия не имею, как мне удалось заставить его работать (кроме тупой удачи и измельчения кода), поэтому мне бы очень понравилось объяснение, почему некоторые из примеров, которые я предоставил, работают, а некоторые нет!
4 ответов
это все еще работает и дает те же результаты. Может ли кто-нибудь объяснить мне, что я удалил, и почему он все еще работает?
вы вызываете неопределенное поведение в C. см. C99 6.3.2.3 указатели / 8:
указатель на функцию одного типа может быть преобразован в указатель на функцию другого тип и обратно; результат должен сравниваться равный исходному указатель. Если преобразованный указатель используется для вызова функции, типа не совместима с заостренным типа, поведение не определено.
в C++, эта программа является некорректным: http://ideone.com/9zRYSj
это все еще "случается работать", потому что compare
функция ожидает пару указателей; и на вашей конкретной платформе sizeof(void*)
это то же самое, что sizeof(int*)
, поэтому вызов указателя функции типа int(void *, void *)
, который фактически содержит указатель на функцию типа int(int *, int *)
is эффективно то же самое, что тип указателя бросает на вашу конкретную платформу в этот конкретный момент времени.
кроме того, мне действительно нужно использовать указатели? Почему я не могу просто сравнить "a" и " b " прямо так (это не работает):
, потому что qsort
принимает общую функцию сравнения для любых двух типов; не только int
. Поэтому он не знает, какой тип, на который указатель разыменован.
по какой-то причине, с многомерным массивом, я смог уйти от использования указателей, и по какой-то причине это сработало! что происходит!
это потому, что следующие прототипы одинаковы:
int foo(int *a, int *b);
int foo(int a[], int b[])
то есть, массив распады в указатель, переданный функции. Явное указание длины массива, как вы сделал:
int foo(int a[2], int b[2])
заставляет компилятор сделать sizeof
и другие биты времени компиляции для обработки элемента как массива из двух элементов; но функция по-прежнему принимает пару указателей, когда она опускается до уровня машины.
в любом из этих случаев, передавая функцию сравнения, которая не принимает пару void *
s приводит к неопределенному поведению. Один из допустимых результатов "неопределенного поведения" - "это просто кажется, что работает". Другим действительным результатом будет "он работает по вторникам" или "он форматирует жесткий диск". Не полагайтесь на такое поведение.
Я не понимаю, почему вам нужны все дополнительные вещи в сравнении функция, поэтому я упростил ее до этого
если вы используете const
квалификатор зависит от вас. Поскольку ожидается, что значения в компараторе не будут изменены. Но можно отбросить const
и нарушить обещание компилятору.
qsort ожидает указатель функции, который принимает два const void *
as параметры, поэтому для функции компаратора передаются указатели:
void qsort(void *base, size_t nmemb, size_t size,
int(*compar)(const void *, const void *));
так передает a
и b
приведет к интерпретации значения как указатели, что очевидно неправильно.
он работает без передачи указателей для многомерных массивов, потому что при передаче массивов они распадаются на указатели. Таким образом, следующий компаратор у вас есть в порядке.
int compare (int a[2], int b[2])
{
return a[1] - b[1];
}
ответ очень прост : из руководства qsort http://pubs.opengroup.org/onlinepubs/009695399/functions/qsort.html прототипом указателя функции является:
int comp(const void *a, const void *b)
typedef, связанный с этим прототипом, может быть объявлен таким образом
typedef int (*comp_qsort_funct_t) ( const void *, const void * )
здесь comp_qsort_funct_t
- Это тип указателя на функцию
прототип функции сравнения qsort использует переменную const, потому что это хорошая практика / дизайн поскольку данные не будут изменены.
использование другого прототипа может привести к неожиданному результату в зависимости от компилятора / платформы. Или просто сбой компиляции, примера, приведенного в комментарий : http://ideone.com/9zRYSj
Так что вы не должны использовать другой прототип.
Я хотел бы подтвердить наблюдения Dlinet, который задал первоначальный вопрос.
компилятор C действительно примет все формы функции compare, даже без const, и даже используя int * вместо void *. Однако мой компилятор C++ отклоняет варианты и принимает только форму const void*.
компилятор C даже принимает форму int, не используя указатели вообще. Я проверил и обнаружил, что форма int функции compare получает указатель значения, а не ints массива. И в результате массив не сортируется, а остается в том же порядке. И я думаю, все это именно то, чего вы ожидали.
таким образом, кажется, что вы можете использовать int * вместо void * в определении функции, а затем не нужно будет бросать внутри функции.
является ли это хорошей практикой программирования является спорным, некоторые программисты говорят Да, а некоторые говорят нет. Мне кажется, хорошим или нет, уменьшение беспорядка было бы точкой в пользу использования int * вместо void *. Но тогда компилятор C++ не позволяет вам этого делать.