你可以转换这种类型的函数指针:
void (*one)(int a)
到这种类型之一:
void (*two)(int a, int b)
然后安全地调用指向的函数以及它已被强制转换的附加参数?我原以为这样的事情是非法的,两种功能都必须兼容。 (意思是相同的原型 - 相同的返回值,相同的参数列表。)但这正是GTK +代码的这一点似乎正在做什么(取自here):
g_signal_connect_swapped(G_OBJECT(button), "clicked",
G_CALLBACK(gtk_widget_destroy), G_OBJECT(window));
如果你查看“clicked”信号(或者只是从第一个链接查看其使用的其他示例),你会看到它的处理程序应该像这样声明:
void user_function(GtkButton *button, gpointer user_data);
当您通过g_signal_connect_swapped()注册处理程序时,窗口小部件指针和数据指针参数按顺序交换,因此,声明应该如下所示:
void user_function(gpointer user_data, GtkButton *button);
这是问题所在。注册为回调的gtk_widget_destroy()函数的原型如下:
void gtk_widget_destroy(GtkWidget *widget);
只采用一个参数。据推测,因为数据指针(一个GtkWindow)和指向信号小部件的指针(一个GtkButton)被交换,它接收的唯一参数将是窗口指针,并且将被忽略的按钮指针将被忽略。一些谷歌搜索已经出现了类似的例子,甚至注册了像gtk_main_quit()这样的函数,它们完全没有参数。
我认为这违反标准是否正确?让GTK +开发人员找到了一些合法的魔力来使这一切都有效吗?
答案 0 :(得分:2)
C调用约定使调用者负责清理堆栈上的参数。因此,如果调用者提供了太多参数,那么这不是问题。其他参数只是被忽略了。
所以是的,你可以使用相同的参数将一个函数指针强制转换为另一个函数指针类型,然后使用相同的参数,并使用太多的参数调用原始函数,它将起作用。
答案 1 :(得分:1)
在我看来,C89这方面的标准非常令人困惑。据我所知,他们不禁止使用没有参数规范的/或功能,所以:
typedef void (*one)(int first);
typedef void (*two)(int first, int second);
typedef void (*empty)();
one src = something;
two dst;
/* Disallowed by C89 standards */
dst = (two) src;
/* Not disallowed by C89 standards */
dst = (two) ((empty) src);
最后,编译器必须能够从one
投射到two
,所以我没有看到禁止直接投射的理由。
无论如何,GTK +中的信号处理在幕后使用一些dark magic来管理具有不同参数模式的回调,但这是一个不同的问题。
答案 2 :(得分:1)
现在,这有多酷,我目前正在通过GTK教程,并且发现了完全相同的问题。
我尝试了几个例子,然后用简化的例子问了问题What happens if I cast a function pointer, changing the number of parameters。
您的问题的答案(改编自上述问题的优秀答案以及问题Function pointer cast to different signature的答案):
所以真正的问题是为什么GLib开发人员会这样做。但我猜这是一个不同的问题......