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
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;
}
typedef int (*FUNC_TYPE_1)(void);
typedef double (*FUNC_TYPE_2)(void);
typedef FUNC_TYPE_1 (*FUNC_TYPE_3)(FUNC_TYPE_2);
而不是:
typedef int (*(*FUNC_TYPE_3)(double (*)(void)))(void);
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 )
并且(事实上)正是我如何在上面产生那种疯狂的混乱 .
27
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;
}
输出是:
22
6
请注意,同样的math_func定义器已用于声明该函数 .
typedef的相同方法可用于extern结构 . (在其他文件中使用sturuct . )
2
这是我作为练习编写的函数指针和函数指针数组的最简单示例 .
typedef double (*pf)(double x); /*this defines a type pf */
double f1(double x) { return(x+x);}
double f2(double x) { return(x*x);}
pf pa[] = {f1, f2};
main()
{
pf p;
p = pa[0];
printf("%f\n", p(3.0));
p = pa[1];
printf("%f\n", p(3.0));
}
63
使用typedef定义更复杂的类型,即函数指针
我将以C语言中定义状态机为例
typedef int (*action_handler_t)(void *ctx, void *data);
现在我们已经定义了一个名为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;
该动作的函数指针看起来像一个简单类型,而typedef主要用于此目的 .
我现在所有的事件处理程序都应该遵循action_handler定义的类型
int handle_event_a(void *fsm_ctx, void *in_msg );
int handle_event_b(void *fsm_ctx, void *in_msg );
7 回答
考虑C标准中的
signal()
函数:完全模糊不清 - 它是一个函数,它接受两个参数,一个整数和一个指向函数的指针,该函数将整数作为参数并且不返回任何内容,并且它(
signal()
)返回一个指向函数的指针,该函数将整数作为参数并且没有回报 .如果你写:
然后您可以将
signal()
声明为:这意味着同样的事情,但通常被认为更容易阅读 . 更清楚的是,该函数采用
int
和SignalHandler
并返回SignalHandler
.不过,这需要一点时间的习惯 . 但是你不能做的一件事就是在函数定义中使用
SignalHandler
typedef
写一个信号处理函数 .我仍然是老派,更喜欢调用函数指针:
现代语法仅使用:
我可以看出为什么会起作用 - 我只是想知道我需要查找变量初始化的位置而不是名为
functionpointer
的函数 .山姆评论说:
typedef void(* SignalHandler)(int signum);
extern SignalHandler信号(int signum,SignalHandler处理程序);
或者,我想问的是,你可以使用什么基础概念来提出你的第二个版本?连接“SignalHandler”和第一个typedef的基础是什么?我认为这里需要解释的是typedef实际上在这里做什么 .
让我们再试一次 . 第一个是直接从C标准中取出 - 我重新输入它,并检查我的括号是否正确(直到我纠正它 - 这是一个难以记住的坚果 Cookies ) .
首先,请记住
typedef
为类型引入了别名 . 因此,别名是SignalHandler
,其类型是:'returns nothing'部分拼写为
void
;作为整数的参数是(我相信)不言自明 . 以下表示法简单地(或不是)C如何指定函数获取指定的参数并返回给定类型:在创建信号处理程序类型之后,我可以使用它来声明变量等等 . 例如:
请注意如何避免在信号处理程序中使用printf()?
那么,我们在这里做了什么 - 除了省略4个标准头文件以使代码编译干净所需?
前两个函数是采用单个整数并且不返回任何内容的函数 . 由于
exit(1);
,其中一个实际上根本没有返回,但是另一个确实在打印消息后返回 . 请注意,C标准不允许您在信号处理程序中做很多事情; POSIX在允许的范围内更加慷慨,但正式并未批准致电fprintf()
. 我还打印出收到的信号 . 在alarm_handler()
函数中,该值将始终为SIGALRM
,因为这是它作为处理程序的唯一信号,但signal_handler()
可能获得SIGINT
或SIGQUIT
作为信号编号,因为两者都使用相同的函数 .然后我创建一个结构数组,其中每个元素标识一个信号编号和要为该信号安装的处理程序 . 我经常担心
SIGHUP
,SIGPIPE
和SIGTERM
以及它们是否被定义(#ifdef
条件编译),但这只会让事情复杂化 . 我也可能使用POSIXsigaction()
而不是signal()
,但这是另一个问题;让我们坚持我们的开始 .main()
函数迭代要安装的处理程序列表 . 对于每个处理程序,它首先调用signal()
以确定进程当前是否忽略该信号,并且这样做时,安装SIG_IGN
作为处理程序,以确保信号保持忽略 . 如果先前没有忽略该信号,则它再次调用signal()
,这次安装首选信号处理程序 . (另一个值可能是SIG_DFL
,信号的默认信号处理程序 . )因为第一次调用'signal()'将处理程序设置为SIG_IGN
而signal()
返回上一个错误处理程序,if
语句之后old
的值必须为SIG_IGN
- 因此断言 . (好吧,如果出现严重问题,可能会SIG_ERR
但是我会从断言解雇中了解到这一点 . )然后程序会正常运行并退出 .
请注意,函数的名称可以视为指向适当类型的函数的指针 . 如果不应用函数调用括号 - 例如在初始值设定项中 - 函数名称将成为函数指针 . 这也是通过
pointertofunction(arg1, arg2)
表示法调用函数的原因 . 当你看到alarm_handler(1)
时,可以认为alarm_handler
是指向函数的指针,因此alarm_handler(1)
是通过函数指针调用函数 .因此,到目前为止,我已经证明
SignalHandler
变量是相对直接使用的,只要你有一些正确的值类型可以分配给它 - 这就是两个信号处理函数提供的 .现在我们回到这个问题 -
signal()
的两个声明如何相互关联 .让我们回顾一下第二个声明:
如果我们更改了函数名称和类型如下:
你将没有问题解释为一个函数,它将一个
int
和一个double
作为参数并返回一个double
值(如果这有问题,你可能会这么做 - 但也许你应该谨慎地提问问题如果它是一个问题,这一个) .现在,
signal()
函数不是double
,而是将SignalHandler
作为其第二个参数,并返回一个作为结果 .这种机制也可以被视为:
很难解释 - 所以我可能搞砸了 . 这次我给出了参数名称 - 尽管这些名称并不重要 .
一般来说,在C中,声明机制是这样的,如果你写:
然后当你写
var
时,它代表给定type
的值 . 例如:在标准中,
typedef
被视为语法中的存储类,而static
和extern
则是存储类 .意味着当您看到
SignalHandler
类型的变量(例如alarm_handler)被调用为:结果有
type void
- 没有结果 . 并且(*alarm_handler)(-1);
是alarm_handler()
的调用,参数为-1
.所以,如果我们宣布:
这意味着:
表示空值 . 因此:
是等价的 . 现在,
signal()
更复杂,因为它不仅返回SignalHandler
,还接受int和SignalHandler
作为参数:如果这仍然让你感到困惑,我不确定如何帮助 - 它仍然在某种程度上对我来说是神秘的,但我已经习惯了它是如何工作的,因此可以告诉你,如果你再坚持25年或者说,它将成为你的第二天性(如果你聪明的话,甚至可能更快) .
函数指针与任何其他指针类似,但它指向函数的地址而不是数据的地址(在堆或堆栈上) . 像任何指针一样,它需要正确输入 . 函数由它们的返回值和它们接受的参数类型定义 . 因此,为了完整地描述函数,必须包含其返回值并接受每个参数的类型 . 当你输入这样一个定义时,你给它一个'友好名称',这使得使用该定义更容易创建和引用指针 .
例如,假设您有一个功能:
然后是以下typedef:
可以用来指向这个
doMulitplication
函数 . 它只是定义一个指向函数的指针,该函数返回一个float并接受两个参数,每个参数都是float类型 . 该定义的友好名称为pt2Func
. 注意pt2Func
可以指向ANY函数,该函数返回一个浮点数并接收2个浮点数 .所以你可以创建一个指向doMultiplication函数的指针,如下所示:
你可以使用此指针调用函数,如下所示:
这很好看:http://www.newty.de/fpt/index.html
一种非常简单的方法来理解函数指针的typedef:
cdecl是一个很好的工具,用于破译像函数指针声明这样的奇怪语法 . 您也可以使用它来生成它们 .
至于使复杂声明的提示更容易解析以供将来维护(由你自己或其他人),我建议制作小块的
typedef
并使用这些小块作为更大和更复杂表达的构建块 . 例如:而不是:
cdecl
可以帮助你解决这个问题:并且(事实上)正是我如何在上面产生那种疯狂的混乱 .
输出是:
22
6
请注意,同样的math_func定义器已用于声明该函数 .
typedef的相同方法可用于extern结构 . (在其他文件中使用sturuct . )
这是我作为练习编写的函数指针和函数指针数组的最简单示例 .
使用typedef定义更复杂的类型,即函数指针
我将以C语言中定义状态机为例
现在我们已经定义了一个名为action_handler的类型,它接受两个指针并返回一个int
定义你的状态机
该动作的函数指针看起来像一个简单类型,而typedef主要用于此目的 .
我现在所有的事件处理程序都应该遵循action_handler定义的类型
参考文献:
Linden的专家C编程