9

我反复遇到在 GTK+ 代码中设置信号处理程序的问题,不需要几个参数,并试图对多个信号使用与处理程序相同的函数,这些信号的处理程序具有不同的签名 - 但使用前 N 个参数(我关心的人)一样。

当这些函数期望的参数少于它们的参数时,将指向函数的指针传递给 GObject API 是否安全(从某种意义上说,它不是未定义的行为,而不是更实用的“它在我的 PC 上工作吗?”)究竟是从信号发射过程中得到的?

或者,将其与 GTK+ 分开,这段代码可以吗?

/* Note: No void *userdata argument! */
void show(int x) {
  printf("x = %d\n", x);
}

void do_stuff(void (*fn)(int, void *), void *userdata) {
  static int total = 0;
  (*fn)(total, userdata);
  total++;
}

void doitnow(void) {
  do_stuff(&show, NULL);
}

为了获得额外的荣誉,请讨论函数签名和调用站点之间不同返回值类型的含义。

编辑:一个几乎相同的问题更密切地探讨了“兼容函数类型”,并得出了一个直接解决我的具体问题的答案——链接 GObject 信号处理程序的问题。TL;DR:是的,这是未定义的行为,但在某些工具包中它实际上是惯用的(尽管不是强制性的)。

4

3 回答 3

8

根据 6.5.2.2 第 9 段,这是明确未定义的行为:

如果函数定义的类型与表示被调用函数的表达式所指向的(表达式的)类型不兼容,则行为未定义。

show定义的类型,

void show(int x)

与调用它的指针所指向的表达式的类型不兼容,

void (*fn)(int, void *)

同样在 6.3.2.3 第 8 段中明确:

指向一种类型的函数的指针可以转换为指向另一种类型的函数的指针,然后再返回;结果应与原始指针比较。如果转换后的指针用于调用类型与引用类型不兼容的函数,则行为未定义。

对于函数,“兼容类型”的特征在 6.7.6.3 (15) [C99 中的 6.7.5.3 (15)] 中:

对于要兼容的两种函数类型,两者都应指定兼容的返回类型。此外,参数类型列表(如果两者都存在)应在参数数量和省略号终止符的使用方面达成一致;相应的参数应具有兼容的类型。如果一种类型具有参数类型列表,而另一种类型由不属于函数定义的函数声明符指定且包含空标识符列表,则参数列表不应有省略号终止符,并且每个参数的类型应与应用默认参数提升所产生的类型兼容。如果一种类型具有参数类型列表,而另一种类型由包含(可能为空)标识符列表的函数定义指定,则两者应在参数数量上一致,并且每个原型参数的类型应与将默认参数提升应用到相应标识符的类型所产生的类型兼容。(在类型兼容性和复合类型的确定中,每个声明为函数或数组类型的参数都被视为具有调整后的类型,每个声明为限定类型的参数被视为具有其声明类型的非限定版本。)

这里最直接相关的部分是参数的数量必须相同。

于 2013-04-24T11:16:56.897 回答
2

这是 C 标准中未定义的行为,但 C 标准还要求支持具有可变参数的函数,编译器实现后者的唯一方法是允许前者。

这种特殊的惯用形式受到 GLib 支持的每个编译器和平台的支持,并且已经存在多年——甚至在 GLib 创建之前——因此它极不可能被破坏。

于 2013-04-24T19:48:08.487 回答
-1

阅读http://www.unixwiz.net/techtips/win32-callconv-asm.htmlhttp://www.csee.umbc.edu/~chang/cs313.s02/stack.shtml 似乎你传递的更多,它不应该引起问题,因为首先要推送参数。所以无论什么不需要,都留在调用者堆栈中。但是相反的方式肯定会引起问题。

于 2013-04-24T11:17:10.840 回答