所以我想知道它们是如何工作的。为了解释我的意思是“函数调用者”,我的意思是glutTimerFunc
的一个很好的例子,它能够接受一个函数作为参数并调用它,即使它不知道它被声明。它是如何做到的?
答案 0 :(得分:6)
您所谈论的内容称为callback,并在C和C ++中使用function pointers实现。
既然你提到了过剩,那么让我们直接从freeglut源代码中获取一个真实的例子。我将使用glutIdleFunc而不是glutTimerFunc,因为代码更简单。
在过剩时,空闲函数回调(你提供给glutIdleFunc的是)一个指向函数的指针,该函数不带任何参数并且什么也不返回。 typedef用于为这样的函数类型提供名称:
typedef void (* FGCBIdle)( void );
这里,FGCBIdle(FreeGlut CallBack Idle的缩写)被定义为指向不带参数且返回类型为void的函数的指针。这只是一种类型定义,它使编写表达式更容易,它不会分配任何存储空间。
Freeglut有一个名为SFG_State的结构,它包含各种设置。该结构的部分定义是:
struct tagSFG_State
{
/* stuff */
FGCBIdle IdleCallback; /* The global idle callback */
/* stuff */
};
struct包含一个FGCBIdle类型的变量,我们建立的是特定函数指针的另一个名称。您可以将IdleCallback字段设置为指向用户使用glutIdleFunc函数提供的函数的地址。该函数的(简化)定义是:
void glutIdleFunc( void (* callback)( void ) )
{
fgState.IdleCallback = callback;
}
这里,fgState是一个SFG_State变量。如您所见,glutIdleFunc接受一个参数,该参数是一个函数指针,该函数指向不带参数且不返回任何内容的函数,此参数的名称为回调函数。在函数内部,全局fgState变量中的IdleCallback设置为用户提供的回调。当你调用glutIdleFunc函数时,你传递了你自己的函数的名称(例如glutIdleFunc(myIdle)),但你真正传递的是函数的地址。
稍后,在glutMainLoop发起的大量过剩事件处理循环中,您将找到以下代码:
if( fgState.IdleCallback )
{
/* stuff */
fgState.IdleCallback( );
}
如果用户提供了空闲回调,则会在循环中调用它。如果您在我的帖子开头检查函数指针教程,您将更好地理解语法,但我希望现在的一般概念更有意义。
答案 1 :(得分:2)
该参数是指向函数的指针。调用者有责任确保函数声明符合要求(例如,参数的数量和类型,调用约定)。
答案 2 :(得分:0)
作为参数传递的函数作为函数指针传递。
在编译代码中,函数只不过是一个CPU可以向量执行的地址。当您传递函数指针时,编译器(以及稍后的链接器)会在调用中插入正确的地址。
函数参数必须完全匹配,因为执行代码将简单地推送堆栈上的值(或寄存器,具体取决于体系结构),并期望弹出(读出)返回值。