C 中函数指针的正确值类型
Correct value type for function pointers in C
我试图理解 C 中的函数指针。在互联网上阅读它时(主要是堆栈溢出 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;
}
我有几个问题
- 在上面的代码中 - 将值赋值给函数指针的正确方法是什么。我看到一些关于堆栈溢出的答案遵循 案例 1 中的约定,我还看到一些答案遵循 案例 2 中的约定。两者似乎都适合我。哪一个是正确的(或更可取的)?
- 此外,为了调用函数指针,我看到了 2 种调用它们的方法 -
math_op(op_a, op_b)
或 (*math_op)(op_a, op_b)
。同样,是否有更好的方法来执行此操作 - 两者似乎都适合我。
我觉得
The expression that denotes the called function shall have type pointer to function returning void or returning an object type other than an array type.
足以回答您的两个问题。函数的名称被隐式转换为 指向函数类型的指针,因此在将函数的地址分配给函数指针时使用 address-of 运算符是多余的。至于通过函数指针调用函数,语法与 "ordinary" 函数调用没有什么不同。
这确实是 C 语言的一个怪癖。*
和(单个)&
在获取函数地址时会被忽略。
int main() {
printf("%p %p %p", (void*)add, (void*)************add, (void*)&add);
}
当"dereferencing"一个函数指针
时,*
被忽略
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));
}
没关系。但是要保持一致。大多数情况下,我没有看到在这种情况下使用 *
或 &
。
情况 1 是首选。
&
是可选的,可以取一个函数的地址(就像:char buf[100]; char *bp = buf;
)
首选情况 1 的原因是 语法 与 相同 用于从函数 [=78] 赋值给函数指针=]地址或另一个函数指针:
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)
作为 shorthand 的原因:(*math_op)(2,23)
更新:
Pointer typedefs are not preferred (by me anyway)
它们肯定有助于函数指针 [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
。其他人只是传递 opaque [给他们] 指针,以便 apiabc
可以对其进行操作。
如果 funcptr
必须改变,改变 a-d
就会有问题(即它们 不是 API 的一部分--只是它的用户)。
一般来说,在指针 typedef
s 上,IMO,它们很好 [=78=]if 它们相当明显并且 一致。
这将是一个用法示例:
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
nodeP
占主导地位的部分在前(即与节点相关)。
IMO,POSIX 声称 whatever_t
作为其类型名称的省是 hubris。但是,发生冲突的可能性很小 [无论如何,对我来说,因为我总是使用可能 不会 冲突的名称。如果是基于未来POSIX类型的碰撞,我输了,必须改变我的定义。
因此,我个人的惯例是:
typedef struct node node_t;
typedef node_t *node_p;
typedef const node_t *node_pc;
只要始终如一地应用,任何此类约定都可以使用。在一组给定的代码中,如果第一次遇到 foo_p
,是的,必须在 .h
文件中查找定义。之后,当看到bar_p
时,约定就已经知道了。
对我来说,这通常不是问题,因为当我遇到不熟悉的代码库时,我总是先查看 .h
文件中的定义 before查看 .c
中的代码
我试图理解 C 中的函数指针。在互联网上阅读它时(主要是堆栈溢出 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;
}
我有几个问题
- 在上面的代码中 - 将值赋值给函数指针的正确方法是什么。我看到一些关于堆栈溢出的答案遵循 案例 1 中的约定,我还看到一些答案遵循 案例 2 中的约定。两者似乎都适合我。哪一个是正确的(或更可取的)?
- 此外,为了调用函数指针,我看到了 2 种调用它们的方法 -
math_op(op_a, op_b)
或(*math_op)(op_a, op_b)
。同样,是否有更好的方法来执行此操作 - 两者似乎都适合我。
我觉得
The expression that denotes the called function shall have type pointer to function returning void or returning an object type other than an array type.
足以回答您的两个问题。函数的名称被隐式转换为 指向函数类型的指针,因此在将函数的地址分配给函数指针时使用 address-of 运算符是多余的。至于通过函数指针调用函数,语法与 "ordinary" 函数调用没有什么不同。
这确实是 C 语言的一个怪癖。*
和(单个)&
在获取函数地址时会被忽略。
int main() {
printf("%p %p %p", (void*)add, (void*)************add, (void*)&add);
}
当"dereferencing"一个函数指针
时,*
被忽略
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));
}
没关系。但是要保持一致。大多数情况下,我没有看到在这种情况下使用 *
或 &
。
情况 1 是首选。
&
是可选的,可以取一个函数的地址(就像:char buf[100]; char *bp = buf;
)
首选情况 1 的原因是 语法 与 相同 用于从函数 [=78] 赋值给函数指针=]地址或另一个函数指针:
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)
作为 shorthand 的原因:(*math_op)(2,23)
更新:
Pointer typedefs are not preferred (by me anyway)
它们肯定有助于函数指针 [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
。其他人只是传递 opaque [给他们] 指针,以便 apiabc
可以对其进行操作。
如果 funcptr
必须改变,改变 a-d
就会有问题(即它们 不是 API 的一部分--只是它的用户)。
一般来说,在指针 typedef
s 上,IMO,它们很好 [=78=]if 它们相当明显并且 一致。
这将是一个用法示例:
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
nodeP
占主导地位的部分在前(即与节点相关)。
IMO,POSIX 声称 whatever_t
作为其类型名称的省是 hubris。但是,发生冲突的可能性很小 [无论如何,对我来说,因为我总是使用可能 不会 冲突的名称。如果是基于未来POSIX类型的碰撞,我输了,必须改变我的定义。
因此,我个人的惯例是:
typedef struct node node_t;
typedef node_t *node_p;
typedef const node_t *node_pc;
只要始终如一地应用,任何此类约定都可以使用。在一组给定的代码中,如果第一次遇到 foo_p
,是的,必须在 .h
文件中查找定义。之后,当看到bar_p
时,约定就已经知道了。
对我来说,这通常不是问题,因为当我遇到不熟悉的代码库时,我总是先查看 .h
文件中的定义 before查看 .c