【发布时间】:2015-07-17 15:38:30
【问题描述】:
考虑以下代码:
#include <iostream>
typedef int (*test_func_t) (int, int, int);
int print_integer (int a)
{
std::cout << "num: " << a << "\n";
return a;
}
int main (int argc, char * argv[])
{
test_func_t func = (test_func_t) &print_integer;
std::cout << "calling with 3 parameters func(5,7,9)\n";
func(5,7,9);
return 0;
}
如您所见,类型 (test_func_t) 定义为具有 3 个 int 参数的函数。函数指针(func)被分配一个指向“print_integer”的指针,它只接收1个参数,然后用3个参数(5、7、9)调用函数指针。
此代码有效并产生“num: 5”输出。
gdb disas 输出(Intel 语法)
disas main
...
0x080486cb <+9>: mov DWORD PTR [esp+0x1c],0x804867d
...
0x080486e0 <+37>: mov DWORD PTR [esp+0x8],0x9
0x080486e8 <+45>: mov DWORD PTR [esp+0x4],0x7
0x080486f0 <+53>: mov DWORD PTR [esp],0x5
0x080486f7 <+60>: mov eax,DWORD PTR [esp+0x1c]
0x080486fb <+64>: call eax
disas print_integer
...
0x08048683 <+6>: mov DWORD PTR [esp+0x4],0x8048830
0x0804868b <+14>: mov DWORD PTR [esp],0x8049ae0
0x08048692 <+21>: call 0x8048530 <std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)@plt>
0x08048697 <+26>: mov edx,DWORD PTR [ebp+0x8]
0x0804869a <+29>: mov DWORD PTR [esp+0x4],edx
0x0804869e <+33>: mov DWORD PTR [esp],eax
0x080486a1 <+36>: call 0x80484d0 <std::ostream::operator<<(int)@plt>
如您所见,其余参数([ebp+0x12] 和 [ebp+0x16])根本没有使用。
我的问题:
- 这似乎适用于具有 __cdecl 调用约定的 Linux x86。在其他架构和调用约定上也安全吗?
- 是否有任何 C/C++ 标准允许/定义从需要较少参数的函数分配函数指针的结果?
这样的使用示例:node.js 的NODE_MODULE 注册了一个函数,其type 有3 个参数[exports, module, priv]。它是called with those 3,但正式示例显示了使用1 或2 参数注册函数。
【问题讨论】:
-
No 和 No. 这是未定义的行为。而且哪里都不安全。
-
它没有明确定义,一般来说,这是一个坏主意,也不安全。尽管如此,它曾经是相当普遍的做法,并且在它工作的调用约定下,它工作得很好。
-
@EugeneSh。 “不安全”——我完全接受。有没有一个例子导致这种类型的调用“破坏”事物? (除了明显错误的使用和滥用)
-
@The-Q 这样的例子可能是
gcc的下一个版本。 当前不一定是不安全的。 -
我确实在 node.js 中看到了这种用法(如原始问题中所述),我无意使用它来实现任何东西。在了解它可能不安全之后,我想知道目前是否可以找到可能破坏某些东西的实际案例。谢谢@EugeneSh。 !
标签: c++ c function-pointers calling-convention