225 c typedef function-pointers
当我读到其他人的代码时,我总是有点难过,这些代码的typedef用于指向带参数的函数的指针.我记得在尝试理解用C语言编写的数值算法时,我花了一些时间来解决这个定义.那么,您是否可以分享您关于如何编写好的typedef指向函数(Do和Do)的提示和想法,为什么它们有用以及如何理解其他人的工作?谢谢!
Jon*_*ler 286
考虑signal()C标准的功能:
extern void (*signal(int, void(*)(int)))(int);
Run Code Online (Sandbox Code Playgroud)
完全模糊不清 - 它是一个函数,它接受两个参数,一个整数和一个指向函数的指针,该函数将整数作为参数并且不返回任何内容,并且it(signal())返回一个指向函数的指针,该函数将整数作为参数并返回没有.
如果你写:
typedef void (*SignalHandler)(int signum);
Run Code Online (Sandbox Code Playgroud)
然后你可以改为声明signal():
extern SignalHandler signal(int signum, SignalHandler handler);
Run Code Online (Sandbox Code Playgroud)
这意味着同样的事情,但通常被认为更容易阅读.更清楚的是该函数采用a int和a SignalHandler并返回a SignalHandler.
不过,这需要一点时间的习惯.不能做的一件事是使用SignalHandler typedef函数定义中的函数编写信号处理函数.
我仍然是老派,更喜欢调用函数指针:
(*functionpointer)(arg1, arg2, ...);
Run Code Online (Sandbox Code Playgroud)
现代语法仅使用:
functionpointer(arg1, arg2, ...);
Run Code Online (Sandbox Code Playgroud)
我可以看出为什么会这样 - 我只是想知道我需要查找变量初始化的位置而不是被调用的函数functionpointer.
山姆评论说:
我以前见过这个解释.然后,就像现在的情况一样,我认为我没有得到的是两个陈述之间的联系:
Run Code Online (Sandbox Code Playgroud)extern void (*signal(int, void()(int)))(int); /*and*/ typedef void (*SignalHandler)(int signum); extern SignalHandler signal(int signum, SignalHandler handler);或者,我想问的是,你可以使用什么基础概念来提出你的第二个版本?连接"SignalHandler"和第一个typedef的基础是什么?我认为这里需要解释的是typedef实际上在这里做什么.
让我们再试一次.第一个是直接从C标准中取出 - 我重新输入它,并检查我的括号是否正确(直到我纠正它 - 这是一个难以记住的坚果饼干).
首先,请记住typedef引入类型的别名.所以,别名是SignalHandler,它的类型是:
指向函数的指针,该函数将整数作为参数并且不返回任何内容.
'没有回报'部分是拼写的void; 作为整数的参数是(我相信)不言自明.以下表示法简单地(或不是)C如何指定函数获取指定的参数并返回给定类型:
type (*function)(argtypes);
Run Code Online (Sandbox Code Playgroud)
在创建信号处理程序类型之后,我可以使用它来声明变量等等.例如:
static void alarm_catcher(int signum)
{
fprintf(stderr, "%s() called (%d)\n", __func__, signum);
}
static void signal_catcher(int signum)
{
fprintf(stderr, "%s() called (%d) - exiting\n", __func__, signum);
exit(1);
}
static struct Handlers
{
int signum;
SignalHandler handler;
} handler[] =
{
{ SIGALRM, alarm_catcher },
{ SIGINT, signal_catcher },
{ SIGQUIT, signal_catcher },
};
int main(void)
{
size_t num_handlers = sizeof(handler) / sizeof(handler[0]);
size_t i;
for (i = 0; i < num_handlers; i++)
{
SignalHandler old_handler = signal(handler[i].signum, SIG_IGN);
if (old_handler != SIG_IGN)
old_handler = signal(handler[i].signum, handler[i].handler);
assert(old_handler == SIG_IGN);
}
...continue with ordinary processing...
return(EXIT_SUCCESS);
}
Run Code Online (Sandbox Code Playgroud)
那么,我们在这里做了什么 - 除了省略4个标准头文件以使代码编译干净所需?
前两个函数是采用单个整数并且不返回任何内容的函数.其中一个实际上根本没有返回,这要归功于它,exit(1);但是另一个确实在打印消息后返回.请注意,C标准不允许您在信号处理程序中做很多事情; POSIX在允许的范围内更加慷慨,但官方并不认可电话fprintf().我还打印出收到的信号.在alarm_handler()函数中,该值将始终SIGALRM作为它是处理程序的唯一信号,但signal_handler()可能获得SIGINT或SIGQUIT作为信号编号,因为两者都使用相同的函数.
然后我创建一个结构数组,其中每个元素标识一个信号编号和要为该信号安装的处理程序.我选择担心3个信号; 我经常担心SIGHUP,SIGPIPE以及SIGTERM它们是否被定义(#ifdef条件编译),但这只会让事情复杂化.我也可能使用POSIX sigaction()代替signal(),但这是另一个问题; 让我们坚持我们的开始.
该main()函数迭代要安装的处理程序列表.对于每个处理程序,它首先调用signal()以查明进程当前是否忽略该信号,并在执行此操作时,安装SIG_IGN为处理程序,以确保信号保持忽略.如果先前没有忽略该信号,则它再次调用signal(),这次安装首选信号处理程序.(另一个值可能是SIG_DFL信号的默认信号处理程序.)因为第一次调用'signal()'将处理程序设置为SIG_IGN并signal()返回先前的错误处理程序,old所以if语句之后的值必须是SIG_IGN- 因此断言.(好吧,SIG_ERR如果出现严重问题,可能会发生错误 - 但是我会从断言解雇中了解到这一点.)
然后程序会正常运行并退出.
请注意,函数的名称可以视为指向适当类型的函数的指针.如果不应用函数调用括号 - 例如在初始值设定项中 - 函数名称将成为函数指针.这也是通过pointertofunction(arg1, arg2)符号调用函数是合理的原因; 当你看到alarm_handler(1),你可以认为这alarm_handler是一个指向函数的指针,因此alarm_handler(1)是通过函数指针调用函数.
因此,到目前为止,我已经证明SignalHandler变量是相对直接使用的,只要你有一些正确的值类型可以分配给它 - 这就是两个信号处理函数提供的.
现在我们回到这个问题 - 两个声明如何signal()相互关联.
让我们回顾一下第二个声明:
extern SignalHandler signal(int signum, SignalHandler handler);
Run Code Online (Sandbox Code Playgroud)
如果我们更改了函数名称和类型如下:
extern double function(int num1, double num2);
Run Code Online (Sandbox Code Playgroud)
你将没有问题解释为一个函数,它接受一个int和一个double参数并返回一个double值(如果这有问题,你可能最好不要错过 - 但也许你应该谨慎地提问问题如果这是一个问题那么这个.)
现在,double该signal()函数不是a,而是将a SignalHandler作为其第二个参数,并返回一个作为结果.
这种机制也可以被视为:
extern void (*signal(int signum, void(*handler)(int signum)))(int signum);
Run Code Online (Sandbox Code Playgroud)
很难解释 - 所以我可能搞砸了.这次我给出了参数名称 - 尽管这些名称并不重要.
一般来说,在C中,声明机制是这样的,如果你写:
type var;
Run Code Online (Sandbox Code Playgroud)
然后当你写var它代表给定的值type.例如:
int i; // i is an int
int *ip; // *ip is an int, so ip is a pointer to an integer
int abs(int val); // abs(-1) is an int, so abs is a (pointer to a)
// function returning an int and taking an int argument
Run Code Online (Sandbox Code Playgroud)
在标准中,typedef被视为语法中的一个存储的类,而不是像static和extern是存储类.
typedef void (*SignalHandler)(int signum);
Run Code Online (Sandbox Code Playgroud)
意味着当你看到一个类型的变量SignalHandler(比如alarm_handler)被调用为:
(*alarm_handler)(-1);
Run Code Online (Sandbox Code Playgroud)
结果type void- 没有结果.并且(*alarm_handler)(-1);是一个alarm_handler()带参数的调用-1.
所以,如果我们宣布:
extern SignalHandler alt_signal(void);
Run Code Online (Sandbox Code Playgroud)
这意味着:
(*alt_signal)();
Run Code Online (Sandbox Code Playgroud)
表示空值.因此:
extern void (*alt_signal(void))(int signum);
Run Code Online (Sandbox Code Playgroud)
是等价的.现在,signal()它更复杂,因为它不仅返回a SignalHandler,它还接受int和a SignalHandler作为参数:
extern void (*signal(int signum, SignalHandler handler))(int signum);
extern void (*signal(int signum, void (*handler)(int signum)))(int signum);
Run Code Online (Sandbox Code Playgroud)
如果这仍然让你感到困惑,我不确定如何帮助 - 它仍然在某种程度上对我来说是神秘的,但我已经习惯了它是如何工作的,因此可以告诉你,如果你再坚持25年或者说,它将成为你的第二天性(如果你聪明的话,甚至可能更快).
psy*_*tik 74
函数指针与任何其他指针类似,但它指向函数的地址而不是数据的地址(在堆或堆栈上).像任何指针一样,它需要正确输入.函数由它们的返回值和它们接受的参数类型定义.因此,为了完整地描述函数,必须包含其返回值并接受每个参数的类型.当你输入这样一个定义时,你给它一个'友好名称',这使得使用该定义更容易创建和引用指针.
例如,假设您有一个功能:
float doMultiplication (float num1, float num2 ) {
return num1 * num2; }
Run Code Online (Sandbox Code Playgroud)
然后是以下typedef:
typedef float(*pt2Func)(float, float);
Run Code Online (Sandbox Code Playgroud)
可以用来指向这个doMulitplication功能.它只是定义一个指向函数的指针,该函数返回一个float并接受两个参数,每个参数都是float类型.这个定义有友好的名字pt2Func.注意,pt2Func可以指向任何返回浮点数并接收2个浮点数的函数.
所以你可以创建一个指向doMultiplication函数的指针,如下所示:
pt2Func *myFnPtr = &doMultiplication;
Run Code Online (Sandbox Code Playgroud)
您可以使用此指针调用该函数,如下所示:
float result = (*myFnPtr)(2.0, 5.1);
Run Code Online (Sandbox Code Playgroud)
这样可以很好地阅读:http://www.newty.de/fpt/index.html
小智 31
一种非常简单的方法来理解函数指针的typedef:
int add(int a, int b)
{
return (a+b);
}
typedef int (*add_integer)(int, int); //declaration of function pointer
int main()
{
add_integer addition = add; //typedef assigns a new variable i.e. "addition" to original function "add"
int c = addition(11, 11); //calling function via new variable
printf("%d",c);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
Car*_*rum 29
cdecl是一个很好的工具,用于解密奇怪的语法,如函数指针声明.您也可以使用它来生成它们.
至于使复杂声明更容易解析以供将来维护的提示(由您自己或其他人),我建议typedef使用小块并使用这些小块作为更大和更复杂表达式的构建块.例如:
typedef int (*FUNC_TYPE_1)(void);
typedef double (*FUNC_TYPE_2)(void);
typedef FUNC_TYPE_1 (*FUNC_TYPE_3)(FUNC_TYPE_2);
Run Code Online (Sandbox Code Playgroud)
而不是:
typedef int (*(*FUNC_TYPE_3)(double (*)(void)))(void);
Run Code Online (Sandbox Code Playgroud)
cdecl 可以帮助你解决这个问题:
cdecl> explain int (*FUNC_TYPE_1)(void)
declare FUNC_TYPE_1 as pointer to function (void) returning int
cdecl> explain double (*FUNC_TYPE_2)(void)
declare FUNC_TYPE_2 as pointer to function (void) returning double
cdecl> declare FUNC_TYPE_3 as pointer to function (pointer to function (void) returning double) returning pointer to function (void) returning int
int (*(*FUNC_TYPE_3)(double (*)(void )))(void )
Run Code Online (Sandbox Code Playgroud)
并且(事实上)正是我如何在上面产生那种疯狂的混乱.
Har*_*ain 12
int add(int a, int b)
{
return (a+b);
}
int minus(int a, int b)
{
return (a-b);
}
typedef int (*math_func)(int, int); //declaration of function pointer
int main()
{
math_func addition = add; //typedef assigns a new variable i.e. "addition" to original function "add"
math_func substract = minus; //typedef assigns a new variable i.e. "substract" to original function "minus"
int c = addition(11, 11); //calling function via new variable
printf("%d\n",c);
c = substract(11, 5); //calling function via new variable
printf("%d",c);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
输出是:
22
6
请注意,同样的math_func定义器已用于声明该函数.
typedef的相同方法可用于extern结构.(在其他文件中使用sturuct.)
小智 7
使用typedef's 来定义更复杂的类型,即函数指针
我将以在 C 中定义状态机为例
typedef int (*action_handler_t)(void *ctx, void *data);
Run Code Online (Sandbox Code Playgroud)
现在我们已经定义了一个名为的类型action_handler,它接受两个指针并返回一个int
定义你的状态机
typedef struct
{
state_t curr_state; /* Enum for the Current state */
event_t event; /* Enum for the event */
state_t next_state; /* Enum for the next state */
action_handler_t event_handler; /* Function-pointer to the action */
}state_element;
Run Code Online (Sandbox Code Playgroud)
指向动作的函数指针看起来像一个简单的类型,typedef主要用于此目的。
我所有的事件处理程序现在都应该遵守由 action_handler
int handle_event_a(void *fsm_ctx, void *in_msg );
int handle_event_b(void *fsm_ctx, void *in_msg );
Run Code Online (Sandbox Code Playgroud)
参考:
Linden 的专家 C 编程
| 归档时间: |
|
| 查看次数: |
261171 次 |
| 最近记录: |