Какая польза от пользовательских данных в функции регистра обратного вызова в c?

В функции обратного вызова регистра есть два аргумента. Один - это указатель на функцию, а второй - userdata.

int callback_register(fn_ptr cb, void *userdata);
//fn_ptr is typedef

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

"userdata" - это именно те произвольные данные, которые вам, Пользователь, необходимо передать в обратный вызов.

Some programmer dude 15.06.2018 12:59

@Someprogrammerdude Будут ли изменены данные пользователя тем, кто выполняет обратный вызов

Omkar Kekre 15.06.2018 13:13

Нет, обычно это не так. Указатель (или другое значение в зависимости от типа) следует просто сохранить, и единственное его использование - передать его вашему обратному вызову. Вот как это обычно делается. Для получения подробной информации вам необходимо прочитать документацию по API, который вы используете.

Some programmer dude 15.06.2018 13:19
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
8
3
1 116
1

Ответы 1

Если все, что у вас есть, это указатель функции, указывающий на функцию обратного вызова, нет способа передать дополнительные данные в функцию обратного вызова.

Например, предположим, что у меня есть строки, которые я хочу отсортировать с помощью qsort. Я мог бы начать с такого простого кода:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define Sizeofarray(a) (sizeof(a) / sizeof(*a))

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

int main()
{
    char *data[] = { "apple", "pear", "1", "2", "10" };
    int i;

    printf("unsorted:\n");
    for(i = 0; i < Sizeofarray(data); i++) printf("%s\n", data[i]);

    qsort(data, Sizeofarray(data), sizeof(*data), compar);

    printf("\nsorted:\n");
    for(i = 0; i < Sizeofarray(data); i++) printf("%s\n", data[i]);
}

int compar(const void *p1, const void *p2)
{
    const char *s1 = *(const char **)p1;
    const char *s2 = *(const char **)p2;
    return strcmp(s1, s2);
}

Эта программа отлично работает и печатает:

unsorted:
apple
pear
1
2
10

sorted:
1
10
2
apple
pear

Но это алфавитная сортировка с использованием strcmp. Предположим, мне нужна опция числовой сортировки (то есть, как стандартная команда Unix / Linux sort с ее опцией -n). И, кроме того, предположим, что я действительно хочу сделать это опцией, управляемой переменной времени выполнения. Я мог бы написать новую, немного более сложную функцию сравнения, например:

int compar2(const void *p1, const void *p2)
{
    const char *s1 = *(const char **)p1;
    const char *s2 = *(const char **)p2;

    if (numeric)
         return atoi(s1) > atoi(s2);
    else return strcmp(s1, s2);
}

Если для флага numeric установлено значение true, входные данные теперь сортируются как

apple
pear
1
2
10

(Слова идут первыми, потому что atoi «конвертирует» их в 0.)

Но тогда жизненно важный вопрос: откуда взялся этот флаг numeric? Если все, что у меня есть, это qsort и функция обратного вызова, которая не принимает определяемый пользователем контекст, у меня нет другого выбора, кроме как сделать флаг numeric глобальной переменной:

int numeric;

int main()
{
    char *data[] = { "apple", "pear", "1", "2", "10" };
    int i;

    printf("unsorted:\n");
    for(i = 0; i < Sizeofarray(data); i++) printf("%s\n", data[i]);

    numeric = 0;
    qsort(data, Sizeofarray(data), sizeof(*data), compar2);

    printf("\nsorted alphabetically:\n");
    for(i = 0; i < Sizeofarray(data); i++) printf("%s\n", data[i]);

    numeric = 1;
    qsort(data, Sizeofarray(data), sizeof(*data), compar2);

    printf("\nsorted numerically:\n");
    for(i = 0; i < Sizeofarray(data); i++) printf("%s\n", data[i]);
}

И это тоже работает. Но, конечно, никому не нравятся глобальные переменные.

Вот здесь и появляется концепция параметра «пользовательские данные». Я не знаю, насколько он стандартен, но моя система предоставляет вариант qsort под названием qsort_r. («r» означает «повторно входим».) В этой версии функции сравнения передается дополнительный аргумент, с которым я могу делать все, что захочу. Здесь я могу сделать его указателем на мой флаг numeric, и теперь мой флаг numeric не обязательно должен быть глобальной переменной:

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

int main()
{
    char *data[] = { "apple", "pear", "1", "2", "10" };
    int i;
    int numeric;

    printf("unsorted:\n");
    for(i = 0; i < Sizeofarray(data); i++) printf("%s\n", data[i]);

    numeric = 0;
    qsort_r(data, Sizeofarray(data), sizeof(*data), &numeric, compar3);

    printf("\nsorted alphabetically:\n");
    for(i = 0; i < Sizeofarray(data); i++) printf("%s\n", data[i]);

    numeric = 1;
    qsort_r(data, Sizeofarray(data), sizeof(*data), &numeric, compar3);

    printf("\nsorted numerically:\n");
    for(i = 0; i < Sizeofarray(data); i++) printf("%s\n", data[i]);
}

int compar3(void *userdata, const void *p1, const void *p2)
{
    int numeric = *(int *)userdata;
    const char *s1 = *(const char **)p1;
    const char *s2 = *(const char **)p2;

    if (numeric)
         return atoi(s1) > atoi(s2);
    else return strcmp(s1, s2);
}

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


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

qsort(data, Sizeofarray(data), sizeof(*data), numeric ? compar_num : compar_alph);

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

Другие вопросы по теме