В функции обратного вызова регистра есть два аргумента. Один - это указатель на функцию, а второй - userdata.
int callback_register(fn_ptr cb, void *userdata);
//fn_ptr is typedef
Во время обратного вызова тот же userdata отправляется обратно в качестве аргумента.
Я понимаю использование отправки указателя функции, но не отправки userdata.
Кто-нибудь может рассказать, как это используется?
@Someprogrammerdude Будут ли изменены данные пользователя тем, кто выполняет обратный вызов
Нет, обычно это не так. Указатель (или другое значение в зависимости от типа) следует просто сохранить, и единственное его использование - передать его вашему обратному вызову. Вот как это обычно делается. Для получения подробной информации вам необходимо прочитать документацию по API, который вы используете.





Если все, что у вас есть, это указатель функции, указывающий на функцию обратного вызова, нет способа передать дополнительные данные в функцию обратного вызова.
Например, предположим, что у меня есть строки, которые я хочу отсортировать с помощью 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 и как его использовать.
"userdata" - это именно те произвольные данные, которые вам, Пользователь, необходимо передать в обратный вызов.