Стандарт C11 §6.3 Преобразования , а точнее §6.3.2.3 Указатели ¶8 говорит:
Указатель на функцию одного тип может быть преобразован в указатель на функцию другого типа и обратно; результат должен сравниваться равным исходному указателю. Если преобразованный указатель используется для вызова функции, тип которой не совместим с указанным типом, поведение не определено.
Код GTK возлагает ответственность за программист на передачу соответствующего типа функции указатель на функцию, принимающую обратный вызов.
g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
Где activate
- это функция, которая возвращает void
, но принимает два параметра. (G_CALLBACK
- это макрос, который просто приводит к GCallback
).
Предположим, что два параметра int
; их тип совпадает с обсуждением.
extern void activate(int, int);
Код в g_signal_connect()
получает 4 указателя. Третий - обратный звонок; формально он имеет тип void (*callback)(void)
.
. Код внутри g_signal_connect()
ожидает обратного вызова с двумя целыми числами (arg1
и arg2
), поэтому его необходимо использовать:
((void (*)(int, int)callback)(arg1, arg2);
для принудительного перевода типа 'generi c' callback
на правильный тип указателя на функцию - в противном случае нельзя избежать вызова неопределенного поведения. Вы должны знать, что для g_signal_connect()
требуется такой указатель, как параметр обратного вызова, приведенный к типу generi c, и вы должны передать ему такой указатель, соответствующий приведенному типу.
Также помните, что Один из способов продемонстрировать «неопределенное поведение» - это «вести себя, как ожидается, даже если ожидания не гарантируются стандартом». Другие способы демонстрации неопределенного поведения включают сбой или повреждение памяти.
Примечание.
C11 §6.2.5 Типы ¶28 говорит:
Указатель на void
должен иметь те же требования к представлению и выравниванию, что и указатель на тип символа. 48) Аналогично, указатели на квалифицированные или неквалифицированные версии совместимые типы должны иметь одинаковые требования к представлению и выравниванию. Все указатели на типы конструкций должны иметь те же требования к представлению и выравниванию, что и другие. Все указатели на типы объединения должны иметь те же требования к представлению и выравниванию, что и другие. Указатели на другие типы не обязательно должны иметь одинаковые требования к представлению или выравниванию.
48) Те же требования к представлению и выравниванию подразумевают взаимозаменяемость в качестве аргументов функций, возвращаемых значений из функций и члены союзов.
Требования §6.3.2.3¶8, по-видимому, подразумевают, что все указатели на разные типы функций должны иметь одинаковые требования к представлению и выравниванию друг с другом; в противном случае становится трудно гарантировать требование о преобразовании туда и обратно в §6.3.2.3¶8.
Другое последствие §6.2.5¶28 состоит в том, что вы не можете надежно преобразовать указатель на тип функции и указатель на тип объекта, такой как void *
. Это имеет последствия для таких функций, как dlsym()
; их трудно использовать чисто - компилятор, вероятно, будет жаловаться, если у вас включены строгие уровни предупреждения.
Компиляция некоторого кода, который преобразует между указателем на функцию и указателем на объект (и наоборот), G CC 9.3 .0 с gcc -std=c99 -O3 -Wall -pedantic -Wdeclaration-after-statement -Wold-style-definition -Wold-style-declaration -Wnested-externs -Wmissing-prototypes -Werror …
дает:
…: error: ISO C forbids conversion of function pointer to object pointer type [-Werror=pedantic]
…: error: ISO C forbids conversion of object pointer to function pointer type [-Werror=pedantic]
Это предупреждение, если у вас не действует -Werror
или -pedantic-errors
, и игнорируется, если у вас нет -pedantic
или -pedantic-errors
в действии.
Остерегайтесь различий между -pedantic
(он же -Wpedantic
) и -pedantic-errors
, как задокументировано G CC в Параметры запроса или подавления предупреждений .