C 可以将指向一种类型的函数的指针强制转换为具有附加参数的另一种类型的函数吗?

C 可以将指向一种类型的函数的指针强制转换为具有附加参数的另一种类型的函数吗?,c,gtk,C,Gtk,是否可以强制转换此类型的函数指针: void (*one)(int a) void (*two)(int a, int b) 对于此类型之一: void (*one)(int a) void (*two)(int a, int b) 然后安全地调用带其他参数的指向函数,这些参数是它被强制转换的?我认为这样的事情是非法的,两种函数类型必须兼容。(意思是相同的原型——相同的返回值,相同的参数列表。)但这正是GTK+代码的作用(取自: 如果您查找“clicked”信号(或仅从第一个链接查看其使

是否可以强制转换此类型的函数指针:

void (*one)(int a)
void (*two)(int a, int b)
对于此类型之一:

void (*one)(int a)
void (*two)(int a, int b)
然后安全地调用带其他参数的指向函数,这些参数是它被强制转换的?我认为这样的事情是非法的,两种函数类型必须兼容。(意思是相同的原型——相同的返回值,相同的参数列表。)但这正是GTK+代码的作用(取自:

如果您查找“clicked”信号(或仅从第一个链接查看其使用的其他示例),您将看到其处理程序的声明如下:

void user_function(GtkButton *button, gpointer user_data);
void gtk_widget_destroy(GtkWidget *widget);
当您通过g_signal_connect_swapped()注册处理程序时,小部件指针和数据指针参数按顺序交换,因此,声明应如下所示:

void user_function(gpointer user_data, GtkButton *button);
问题就在这里。注册为回调的gtk_widget_destroy()函数的原型如下:

void user_function(GtkButton *button, gpointer user_data);
void gtk_widget_destroy(GtkWidget *widget);
只接受一个论点。据推测,因为数据指针(GtkWindow)和指向信令小部件的指针(GtkButton)是交换的,所以它接收的唯一参数将是窗口指针,随后传递的按钮指针将被忽略。一些谷歌搜索也发现了类似的例子,甚至是注册像gtk_main_quit()这样完全不带参数的函数


我认为这违反了标准,对吗?GTK+开发人员是否找到了一些法律魔法来让这一切正常工作?

C调用约定使调用方有责任清理堆栈上的参数。因此,如果调用者提供了太多的参数,这不是问题。其他参数被忽略


因此,是的,您可以将一个函数指针强制转换为另一个具有相同参数的函数指针类型,然后再转换一些参数,然后使用太多的参数调用原始函数,它将正常工作

在我看来,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+中的信号处理使用一些幕后操作来管理具有不同参数模式的回调,但这是一个不同的问题。

现在,这有多酷,我目前也在努力学习GTK教程,并遇到了完全相同的问题

我试了几个例子,然后用一个简化的例子提问

您问题的答案(改编自我上述问题的优秀答案和问题的答案):

  • 是的,这违反了C标准。如果将函数指针强制转换为不兼容类型的函数指针,则必须在调用它之前将其强制转换回其原始(或兼容)类型。其他任何东西都是未定义的行为
  • 然而,它在实践中的作用取决于C编译器,特别是它使用的调用约定。最常见的调用约定(至少在i386上)恰好是将参数以相反的顺序放在堆栈上,因此,如果函数需要的参数少于它提供的参数,它将只使用第一个参数而忽略其余的参数——这正是您想要的。然而,这将在具有不同调用约定的平台上打破

所以真正的问题是为什么能说会道的开发者会这样做。但我想这是另一个问题……

Dupe:事实上,我相信没有所谓的“C调用约定”。C标准没有定义它,所以它取决于编译器和平台使用什么约定。但是,大多数编译器/平台使用您描述的调用约定。