我试图理解C中的函数指针。在Internet上阅读它时(主要是堆栈溢出QA)-我遇到了两种可以为函数指针赋值的方法
#include <stdio.h>
double add(int a, double b) {
return a + b;
}
double subtract(int a, double b) {
return a - b;
}
int main() {
int op_a = 23;
double op_b = 2.9;
// Case 1. Just pass the function (not its address)
double (*math_op)(int a, double b) = add;
printf("The output of Math Operation %f\n", math_op(op_a, op_b));
// Case 2. Pass the function's address
math_op = &subtract;
printf("The output of Math Operation %f\n", math_op(op_a, op_b));
printf("End of program\n");
return 0;
}
我有几个问题
math_op(op_a, op_b)
或(*math_op)(op_a, op_b)
。再次,有没有一种更好的方法可以这样做-两者似乎都对我来说是正确的。答案 0 :(得分:3)
我认为
表示被调用函数的表达式必须具有指向函数的类型指针,该函数返回void或返回除数组类型以外的其他对象类型。
足以回答您的两个问题。函数名称隐式转换为函数类型指针,因此在将函数的地址分配给函数指针时使用address-of运算符是多余的。对于通过函数指针调用函数,语法与“常规”函数调用没有什么不同。
答案 1 :(得分:1)
这实际上是C的怪癖。*
和(单个)&
在获取函数地址时会被忽略。
int main() {
printf("%p %p %p", (void*)add, (void*)************add, (void*)&add);
}
“解引用”函数指针时,*
被忽略
int main() {
double (*math_op)(int a, double b) = add;
printf("%p %p %f", (void*)math_op, (void*)*************math_op, (***************math_op)(1, 1.0));
}
没关系。但是要保持一致。通常,在这种情况下,我看不到*
和&
也没有使用。
答案 2 :(得分:0)
首选案例1。
&
是可选的,用于获取函数的地址(就像:char buf[100]; char *bp = buf;
)
首选案例1的原因是,语法是 same ,用于从函数 address 或其他函数分配给函数指针指针:
typedef double (*math_func)(int a, double b);
math_func math_op = add;
math_func math_op2 = sub;
math_func math_op3 = math_op2;
类似地,这就是为什么您可以说:math_op(2,23)
作为(*math_op)(2,23)
更新:
指针typedef不是首选(无论如何对我来说就是这样)
它们肯定有助于函数指针[Jonathan Leffler例外]。
如果您有(例如)10个需要带参数作为函数指针的函数,您是否想在所有10个函数原型中都将其拼写出来,特别是如果函数指针本身带8个参数呢?
假设[在某些API中]您有一个函数指针:
typedef void (*funcptr)(int x,int y,int z);
请考虑您具有以下形式的功能:
void func(funcptr f,...);
而且,您具有以下函数的调用链:
a -> b -> c -> d -> apiabc
函数a-d
不会调用f
,而只能执行apiabc
。只有apiabc
知道如何正确调用f
。其他指针只是沿apiabc
可以对其执行操作。
如果必须funcptr
进行更改,则更改a-d
会出现问题(即它们不是API的一部分-仅是API的用户)。
在一般情况下,对于IMO指针typedef
, 如果它们相当明显且一致,则它们很好。
这将是用法的一个示例:
typedef struct node *Node; // poor
typedef struct node *pNode; // MS (common but, IMO, poor)
typedef struct node *nodeP; // okay
typedef struct node *nodeptr; // better?
typedef const struct node *nodecptr; // better?
IMO,MS版本很差,因为如果我们对所有名称pNode
进行排序,它们将与所有[无关] pWhatever
混合在一起,而不会吸引到node
,而从左向右{{ 1}}首先是主要部分(即,它与节点有关)。
IMO,POSIX将nodeP
声明为其类型名称的省份是 hubris 。但是,发生冲突的可能性很小(无论如何,对我来说,因为我总是使用可能不会冲突的名称)。如果基于未来 POSIX类型存在 冲突,我会迷失方向,必须更改自己的定义。
因此,我的个人习惯是:
whatever_t
只要一致地应用,任何此类约定都可以使用。在给定的代码集中,如果一个人第一次遇到typedef struct node node_t;
typedef node_t *node_p;
typedef const node_t *node_pc;
,是的,必须在foo_p
文件中查找定义。此后,当人们看到.h
时,就知道该约定。
对我来说,这通常不是问题,因为当我遇到不熟悉的代码库时,我总是先查看bar_p
文件中的定义,然后再查看中的代码。 .h