Каковы параметры в этом вызове функции C qsort? - PullRequest
4 голосов
/ 09 февраля 2010
qsort(bt->rw[t], bt->num[t], 
      sizeof(TRELLIS_ATOM *), 
      (int (*)(const void *,const void *))compare_wid);

bt->rw[t] - указатель на указатель структуры, bt->[num] - int, я не понимаю, что это за четвертый параметр, за исключением того, что compare_wid - это функция, определенная где-то следующим образом:

static int compare_wid( TRELLIS_ATOM* a, TRELLIS_ATOM* b )
{
   ...
   return x;
}

Ответы [ 8 ]

22 голосов
/ 09 февраля 2010

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

Вы предоставляете:

  • базовый указатель, который указывает на начало непрерывного блока данных,
  • количество элементов в блоке
  • размер одного элемента данных и
  • функция, которая сравнивает два значения данных.

Поскольку алгоритм сортировки в общем случае не зависит от типа сортируемых данных, qsort() можно записать без знания типов данных, которые он сортирует. Но чтобы сделать это, qsort() принимает void * параметров, что означает "универсальный указатель" в C.

Допустим, у вас есть массив несортированных int значений:

#define N 1024
int data[N] = { 10, 2, 3, -1, ... } /* 1024 values */

Затем вы можете отсортировать их, позвонив по номеру qsort():

qsort(data, N, sizeof data[0], compare_int);

data имеет тип int * при передаче в qsort(), а первый параметр qsort() имеет тип void *. Поскольку любой указатель объекта может быть преобразован в void * в C, это нормально. Следующие два аргумента тоже в порядке. Последний аргумент, compare_int, должен быть функцией, которая принимает два const void * параметра и возвращает int. Функция будет вызываться qsort() с парами указателей от &data[0] до &data[N-1] столько раз, сколько необходимо.

Чтобы объявить функцию f(), которая "принимает два const void * параметра и возвращает int":

int f(const void *, const void *);

Если кто-то хочет объявить указатель функции, который мы можем установить на f, указатель объявляется как:

int (*pf)(const void *, const void *);
pf = f;

Требуются круглые скобки, потому что в противном случае pf будет функцией, возвращающей int *. Теперь pf - указатель на функцию, возвращающую int.

Возвращаясь к нашему int алгоритму сортировки, и, исходя из вышесказанного, мы можем определить compare_int() как:

int compare_int(const void *a, const void *b)
{
    const int *the_a = a;
    const int *the_b = b;
    if (*the_a > *the_b) return 1;
    else if (*the_a < *the_b) return -1;
    else return 0;
}

При написании compare_int() мы знаем, что переданные указатели int * замаскированы под void *, поэтому мы конвертируем их обратно в int *, что в порядке, и затем мы сравниваем числа.

Теперь обратим внимание на рассматриваемый код:

static int compare_wid( TRELLIS_ATOM* a, TRELLIS_ATOM* b )

означает, что compare_wid - это функция, которая принимает два параметра TRELLIS_ATOM * и возвращает int. Как мы только что видели, последний аргумент qsort() должен быть функцией типа:

int (*)(const void *, const void *)

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

Однако у этого есть проблемы. Функция типа:

int (*)(TRELLIS_ATOM *, TRELLIS_ATOM *)

не эквивалентно функции типа:

int (*)(const void *, const void *)

, поэтому не гарантируется, что приведение будет работать. Намного проще, правильнее и стандартнее объявить compare_wid() как:

static int compare_wid(const void *a, const void *b);

И определение compare_wid() должно выглядеть так:

static int compare_wid(const void *a, const void *b)
{
    const TRELLIS_ATOM *the_a = a;
    const TRELLIS_ATOM *the_b = b;
    ...
    /* Now do what you have to do to compare the_a and the_b */
    return x;
}

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

Если вы не можете изменить compare_wid(), напишите другую функцию:

static int compare_stub(const void *a, const void *b)
{
    return compare_wid(a, b);
}

и звоните qsort() с compare_stub() (без приведения) вместо compare_wid().

Редактировать : Основываясь на многих неправильных ответах, вот тестовая программа:

$ cat qs.c
#include <stdio.h>
#include <stdlib.h>

struct one_int {
    int num;
};

#ifdef WRONG
static int compare(const struct one_int *a, const struct one_int *b)
{
#else
static int compare(const void *a_, const void *b_)
{
    const struct one_int *a = a_;
    const struct one_int *b = b_;
#endif
    if (a->num > b->num) return 1;
    else if (a->num < b->num) return -1;
    else return 0;
}

int main(void)
{
    struct one_int data[] = {
        { 42 },
        { 1 },
        { 100 }
    };
    size_t n = sizeof data / sizeof data[0];

    qsort(data, n, sizeof data[0], compare);
    return 0;
}

Компиляция с compare() определяется как принятие двух const struct one_int * значений:

$ gcc -DWRONG -ansi -pedantic -W -Wall qs.c
qs.c: In function `main':
qs.c:32: warning: passing argument 4 of `qsort' from incompatible pointer type

Компиляция с правильным определением:

$ gcc -ansi -pedantic -W -Wall qs.c
$

Редактировать 2 : Кажется, существует некоторая путаница относительно законности использования compare_wid как есть для окончательного аргумента qsort(). В comp.lang.c FAQ, вопрос 13.9 есть хорошее объяснение (выделено мной):

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

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

Как уже упоминалось в FAQ, см. Также this .

5 голосов
/ 09 февраля 2010

(int (*)(const void *,const void *)) означает «обрабатывать последующее как указатель на функцию, которая принимает два параметра типа const void* и возвращает int». compare_wid действительно функция, которую можно обрабатывать таким образом.

qsort вызовет эту функцию для выполнения сравнения элементов при сортировке: если целое число, которое она возвращает, равно нулю, элементы предполагаются равными, в противном случае для их упорядочения используется знак целого числа.

3 голосов
/ 09 февраля 2010

QSort

void qsort ( void * base, size_t num, size_t size, 
                    int ( * comparator ) ( const void *, const void * ) );

компаратор : функция, которая сравнивает два элементы. Функция должна следовать этот прототип:

int компаратор (const void * elem1, const void * elem2);

Функция должна принимать два параметры, которые являются указателями на элементы, отлитые по типу как недействительные *. Эти параметры должны быть возвращены к некоторым Тип данных и сравнивать.

Возвращаемое значение этой функции должен представлять, является ли elem1 считается меньше, равно или больше, чем elem2, возвращаясь, соответственно отрицательное значение, ноль или положительное значение.

2 голосов
/ 09 февраля 2010

Четвертый параметр содержит явное приведение к указателю на функцию:

int (*)(const void *,const void *)
  • часть int соответствует типу возврата
  • часть (*) является обозначением для указателя функции
  • часть (const void *,const void *) - это типы параметров
0 голосов
/ 16 апреля 2010
/* Integer comparison function for use with the stdlib's Qsort. */
inline int int_compare(const void *p1, const void *p2) {
        return ( *(int*)p1 - *(int*)p2 );
}
0 голосов
/ 09 февраля 2010

Это функция быстрой сортировки, для которой требуется указатель функции на пользовательскую процедуру обработки сортировки. Следовательно, приведение типов для параметра указателя функции в четвертом параметре.

Когда вызывается функция быстрой сортировки, она выполняет указатель функции, который вы указали для обработки алгоритма сортировки, то есть TRELLIS_ATOM *a и TRELLIS_ATOM *b, и затем вы делаете проверку сравнения между ними и возвращаете , 0 или -1 в логическом сравнении, например:

if (*a > *b) return 1;
if (*a == *b) return 0;
if (*a < *b) return -1;

Надеюсь, это поможет, С наилучшими пожеланиями, Том.

0 голосов
/ 09 февраля 2010

Это пользовательская функция сортировки для элементов типа TRELLIS_ATOM (что бы это ни было).

compare_wid должно возвращать что-то <0, если <code>*a < *b,> 0, если *a > *b, и 0, если *a и *b логически считаются равными.

0 голосов
/ 09 февраля 2010

Это функция предиката, которая вызывается реализацией qsort для сравнения двух TRELLIS_ATOM*.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...