Что означает этот синтаксис C? - PullRequest
6 голосов
/ 09 сентября 2011

Это из библиотеки 'magic', которую я использую.

void
sort(magic_list *l, int (*compare)(const void **a, const void **b))
{
    qsort(l->list, l->num_used, sizeof(void*),
         (int (*)(const void *,const void *))compare);
}

Мой вопрос: что на последнем аргументе делает qsort?

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

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

Ответы [ 6 ]

8 голосов
/ 09 сентября 2011

Это именно то, что делает приведенный вами актерский состав: он преобразует указатель типа

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

в указатель типа

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

Последнее - то, что ожидается от qsort.

Подобные вещи встречаются довольно часто в коде плохого качества.Например, когда кто-то хочет отсортировать массив int s, он часто пишет функцию сравнения, которая принимает указатели на int *

int compare_ints(const int *a, const int *b) {
  return (*a > *b) - (*a < *b);
}

, и когда приходит время фактически вызвать qsort, онипринудительно приведите его к нужному типу, чтобы подавить жалобы компилятора

qsort(array, n, sizeof *array, (int (*)(const void *,const void *)) compare_ints);

Это «взлом», который приводит к неопределенному поведению.Это, очевидно, плохая практика.То, что вы видите в своем примере, является просто менее прямой версией того же «хака».

В таких случаях правильным подходом было бы объявить функцию сравнения как

int compare_ints(const void *a, const void *b) {
  int a = *(const int *) a;
  int b = *(const int *) b;
  return (a > b) - (a < b);
}

, а затемиспользуйте его без каких-либо приведений

qsort(array, n, sizeof *array, compare_ints);

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

3 голосов
/ 09 сентября 2011

Последний аргумент qsort - приведение указателя на функцию, принимающего двойные указатели, к указателю, принимающему одиночные указатели, которые примет qsort. Это просто актерский состав.

2 голосов
/ 09 сентября 2011

На большинстве аппаратных средств можно предположить, что все указатели выглядят одинаково на аппаратном уровне.Например, в системе с плоскими 64-битными адресными указателями всегда будет 64-битное целое число.То же самое относится и к указателям на указатели или указателям на указатели на указатели на указатели.

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

qsort обрабатывает указатели в общем, как будто каждый из них непрозрачен.Так что это не знает или все равно, как они разыменовываются.Он знает, в каком порядке они находятся в данный момент, и использует аргумент сравнения, чтобы определить, в каком порядке они должны быть.

Используемая вами библиотека предположительно хранит списки указателей на указатели.Он имеет функцию сравнения, которая может сравнивать два указателя с указателями.Таким образом, он передает это, чтобы перейти к qsort.Это просто синтаксически лучше, чем, например,

qsort(l->list, l->num_used, sizeof(void*), compare);

/* elsewhere */

int compare(const void *ptr1, const void *ptr2)
{
    // these are really pointers to pointers, so cast them across
    const void **real_ptr1 = (const void **)ptr1;
    const void **real_ptr2 = (const void **)ptr2;

    // do whatever with real_ptr1 and 2 here, e.g.
    return (*real_ptr2)->sort_key - (*real_ptr1)->sort_key;
}
1 голос
/ 09 сентября 2011

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

0 голосов
/ 09 сентября 2011

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

0 голосов
/ 09 сентября 2011

(int (*)(const void *,const void *))compare - это приведение в стиле C для приведения указателя функции compare к указателю функции с двумя const void * аргументами.

...