Globals: лучший вариант, когда параметры функции обратного вызова не предоставляют достаточно информации в C? - PullRequest
2 голосов
/ 22 июня 2010

Давайте возьмем в качестве примера функцию обратного вызова сравнения qsort ()

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

Что происходит, когда результат функции сравнения зависит от текущего значения переменной?Похоже, что у меня есть только два варианта: использовать глобальную переменную (yuck) или обернуть каждый элемент несортированного массива в структуру, содержащую дополнительную информацию (double yuck).

Поскольку qsort () является стандартной функцией, я весьма удивлен, что она не позволяет передавать дополнительную информацию;что-то вроде аргумента execv() в конце char *const argv[].1011 *, будучи двумя другими, у меня была эта проблема.

Я просто "делаю это неправильно" или это обычная проблема, и я приписал недосмотр этим типам определений функций обратного вызова?

РЕДАКТИРОВАТЬ

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

Вот рабочий пример использования глобальной переменной «x».Как бы вы предложили мне сделать это с помощью нескольких функций обратного вызова?

#include <stdint.h>
#include <stdio.h>
#include <math.h>

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

int x;

int main(void)
{
    int i;
    int bins[6] = { 140, 100, 180, 80, 240, 120 };

    x = 150;

    qsort(bins, 6, sizeof(int), bin_cmp);

    for(i=0; i < 6; i++)
       printf("%d ", bins[i]);

    return 0;
}

int bin_cmp(const void* a, const void* b)
{
    int a_delta = abs(*(int*)a - x);
    int b_delta = abs(*(int*)b - x);

    if ( a_delta == b_delta )
        return 0;

    return a_delta < b_delta ? -1 : 1;
}

Вывод

140 180 120 100 80 240 

Ответы [ 7 ]

2 голосов
/ 23 июня 2010

Изменить значение указателя функции. Весь смысл (не каламбур) указателей функций заключается в том, что пользователь может передавать различные функции при разных обстоятельствах. Скорее, передача одной функции, которая действует по-разному в зависимости от внешних обстоятельств, и передача различных функций в зависимости от внешних обстоятельств.

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

В ответ на ваше редактирование

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

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

1 голос
/ 23 июня 2010

Вы можете написать свою собственную специализированную реализацию qsort и настроить ее под свои нужды. Чтобы дать вам представление о том, насколько оно короткое, это реализация qsort с вашей дельтой.

void swap(int *a, int *b)
{
  int t=*a; *a=*b; *b=t;
}
void yourQsort(int arr[], int beg, int end, int delta)
{
  if (end > beg + 1)
  {
    int piv = arr[beg], l = beg + 1, r = end;
    while (l < r)
    {
      //Here use your var something like this
      int a_delta = abs(arr[l] - delta);
      int b_delta = abs(piv - delta);
      if (a_delta <= delta)
        l++;
      else
        swap(&arr[l], &arr[--r]);
    }
    swap(&arr[--l], &arr[beg]);
    yourQsort(arr, beg, l, delta);
    yourQsort(arr, r, end, delta);
  }
}

Более C-оптимизированные реализации называются здесь .

1 голос
/ 23 июня 2010

Похоже, вам нужны функции bsearch_s() и qsort_s(), определенные TR 24731-1 :

§6.6.3.1 Функция bsearch_s

Синопсис

#define __STDC_WANT_LIB_EXT1__ 1
#include <stdlib.h>
void *bsearch_s(const void *key, const void *base,
                rsize_t nmemb, rsize_t size,
                int (*compar)(const void *k, const void *y, void *context),
                void *context);

§6.6.3.2 Функция qsort_s

Синопсис

#define __STDC_WANT_LIB_EXT1__ 1
#include <stdlib.h>
errno_t qsort_s(void *base, rsize_t nmemb, rsize_t size,
                int (*compar)(const void *x, const void *y, void *context),
                void *context);

Обратите внимание, что интерфейс имеет нужный вам контекст.

Что-то довольно близкое к этому должно быть доступно в MS Visual Studio system.

1 голос
/ 23 июня 2010

Я бы слушал Тлейтона и заключил эту логику в функцию, которая возвращает указатель на соответствующую функцию сравнения.

1 голос
/ 23 июня 2010

С подписью qsort, я думаю, ваши возможности довольно ограничены. Вы можете использовать глобальную переменную или обернуть свои элементы в структуры, как вы предлагали, но я не знаю ни одного хорошего "чистого" способа сделать это в C. Я думаю, что есть другие решения, но они не будут чище, чем использование глобальных переменных. Если ваше приложение однопоточное, я бы сказал, что это ваша лучшая ставка, если вы осторожны с глобальными именами.

0 голосов
/ 23 июня 2010

ИМО, иди с глобальным.Проблема в том, что вы действительно просите qsort сделать две вещи, и сортировать, и сопоставить, где он предназначен только для первой.

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

struct sort_element {
    int delta; // This is the delta value 
    int index; // This is the index of the value in the source array
 }

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

0 голосов
/ 23 июня 2010

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

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