调用具有比预期更多参数的C函数是否安全?

时间:2013-04-24 10:53:13

标签: c gtk gobject

我反复讨论在GTK +代码中设置信号处理程序的问题,不需要几个参数,并试图使用与多个信号处理程序相同的函数,其处理程序具有不同的签名 - 但是第一个N个论点(我关心的那些)相同。

它是否安全(在某种意义上说它不是未定义的行为,而不是更实用的“它在我的PC上工作吗?”)将指针传递给GObject API时,这些函数需要更少的参数他们实际上会从信号发射过程中获得什么?

或者,要将此与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);
}

要获得额外的功劳,请讨论功能签名和呼叫站点之间不同返回值类型的含义。

编辑: almost-identical question探测“兼容的函数类型”更接近,并绘制an answer directly addressing my concrete problem - 链接GObject信号处理程序。 TL; DR:是的,它是未定义的行为,但它在某些工具包中实际上是惯用的(尽管不是强制性的)。

3 个答案:

答案 0 :(得分: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)]:

  

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

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

答案 1 :(得分:2)

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

这个特殊的惯用形式得到了GLib支持的每个编译器和平台的支持,并且已经存在多年 - 甚至在创建GLib之前 - 所以它不太可能被破坏。

答案 2 :(得分:-1)

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