(注意:链接另一个显示函数指针示例的答案无济于事。我的问题恰恰是关于不同答案中显示的多种方式,并试图理解它们之间的差异)
我试图了解将函数作为参数传递给C中其他函数的正确方法(无C ++)。我已经看到了几种不同的方式,但这些差异对我来说并不明显。
我正在运行macOS
我的编译器是GCC:
$ gcc --version
Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk/usr/include/c++/4.2.1
Apple LLVM version 10.0.1 (clang-1001.0.46.4)
Target: x86_64-apple-darwin18.7.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
我CFLAGS=-Wall -g -O0
在我的Makefile中使用。
以下4个摘要均产生相同的结果(至少具有相同的可见输出)。请注意,样本之间的唯一区别在于execute
函数的声明和调用。我已经在引号中包括了我最初调用这些样本中的每个样本的方式,只是为了区分它们(因此命名可能是错误的)。
它们实际上只是以下四个排列的全部:
execute
要接收的功能void f()
或void (*f)()
execute
用execute(print)
或调用函数execute(&print)
请注意,在所有情况下,该函数都是通过f()
而不是调用的(*f)()
。但是我也用(*f)()
进行了测试,得出了相同的结果。因此,实际上是8个排列(为简洁起见,这里仅显示4个)
片段1:“传递而不指针,接收无指针”
#include <stdio.h>
void execute(void f()) {
printf("2 %p %lu\n", f, sizeof(f));
f();
}
void print() {
printf("Hello!\n");
}
int main() {
printf("1 %p %lu\n", print, sizeof(print));
execute(print);
return 0;
}
片段2:“通过用一个指针,并接收与一个指针”
#include <stdio.h>
void execute(void (*f)()) {
printf("2 %p %lu\n", f, sizeof(f));
f();
}
void print() {
printf("Hello!\n");
}
int main() {
printf("1 %p %lu\n", print, sizeof(print));
execute(&print);
return 0;
}
片段3:“通过用一个指针,并且接收没有一个指针”
#include <stdio.h>
void execute(void (f)()) {
printf("2 %p %lu\n", f, sizeof(f));
f();
}
void print() {
printf("Hello!\n");
}
int main() {
printf("1 %p %lu\n", print, sizeof(print));
execute(&print);
return 0;
}
片段4:“传递没有一个指针,并接收与一个指针”
#include <stdio.h>
void execute(void (*f)()) {
printf("2 %p %lu\n", f, sizeof(f));
f();
}
void print() {
printf("Hello!\n");
}
int main() {
printf("1 %p %lu\n", print, sizeof(print));
execute(print);
return 0;
}
对于所有示例:
Hello
正确打印%p
第一张和第二张中打印的值相同sizeof
在第一个打印中始终为1sizeof
在第二个打印中始终为8我已经阅读了几个示例(Wikipedia,StackOverflow,以及StackOverflow答案中链接的其他资源),其中有很多显示了不同的示例。我的质询恰恰是为了理解这些差异。
的约函数指针维基百科文章示出了类似于代码段4的例子(通过我简化):
#include <math.h>
#include <stdio.h>
double compute_sum(double (*funcp)(double), double lo, double hi) {
// ... more code
double y = funcp(x);
// ... more code
}
int main(void) {
compute_sum(sin, 0.0, 1.0);
compute_sum(cos, 0.0, 1.0);
return 0;
}
注意:
compute_sum(sin, 0.0, 1.0)
(不带 &
on sin
)传递double (*funcp)(double)
(带有 *
)funcp(x)
(不带 *
,因此不带(*funcp)(x)
)同一篇Wikipedia文章中的下一个示例告诉我们,&
不需要传递函数的时间,没有任何进一步的解释:
// This declares 'F', a function that accepts a 'char' and returns an 'int'. Definition is elsewhere.
int F(char c);
// This defines 'Fn', a type of function that accepts a 'char' and returns an 'int'.
typedef int Fn(char c);
// This defines 'fn', a variable of type pointer-to-'Fn', and assigns the address of 'F' to it.
Fn *fn = &F; // Note '&' not required - but it highlights what is being done.
// ... more code
// This defines 'Call', a function that accepts a pointer-to-'Fn', calls it, and returns the result
int Call(Fn *fn, char c) {
return fn(c);
} // Call(fn, c)
// This calls function 'Call', passing in 'F' and assigning the result to 'call'
int call = Call(&F, 'A'); // Again, '&' is not required
// ... more code
注意:
Call(&F, 'A')
(与 &
on F
)传递Fn *fn
(带有 *
)fn(c)
(不带 (*fn)
)这个答案:
func(print)
(不带 &
on print
)传递void (*f)(int)
(带有 *
)(*f)(ctr)
(与 (*fn)
)调用该答案显示了2个示例:
execute(print)
(不带 &
)传递void f()
(不带 *
)f()
(不带 *
)execute(&print)
(与 &
)传递void (*f)()
(带有 *
)f()
(不带 *
)这个答案:
&
)int (*functionPtr)(int, int)
(带有 *
)(*functionPtr)(2, 3)
(与 *
)调用我在一个答案中找到了这种链接的材料(实际上是C ++,但是它没有使用任何有关函数指针的C ++特有的东西):
&Minus
(与 &
)传递float (*pt2Func)(float, float)
(带有 *
)pt2Func(a, b)
(不带 *
)我在这里提供了7个示例,这些示例提供了使用或不使用&
以及*
将函数作为参数传递/将函数作为参数接收/将函数作为参数接收的至少5种组合。
我相信上一节表明,关于StackOverflow上最相关的问题/答案以及我已链接的其他资料(大多数都链接在StackOverflow的答案中),没有达成共识的解释。
似乎所有这四种方式都由编译器完全相同地处理,或者在如此简单的示例中没有出现非常细微的差异。
我知道为什么第二张打印sizeof(f)
要8
在摘要2和摘要4中进行打印。这就是我的64位系统中的指针大小)。但是我不明白,即使在execute
函数声明参数不带a的情况下*
(代码段1和3),第二张打印结果也会打印出指针的大小,也不明白为什么在第一张打印结果中函数变量sizeof
等于1
。
f(myFunc)
并实际使用f(&myFunc)
,或类似的东西。我想知道哪个是“规范”方式)。f()
或调用通过参数接收的传递函数(*f)()
吗?sizeof(print)
)的大小始终为1
?在这种情况下,我们实际得到的sizeof是多少?(显然不是指针的大小,在我的64位计算机上为8字节。如果使用,我将获得指针的大小sizeof(&print)
)。sizeof(f)
,即使参数被声明为void (f)()
(因此不带*
,我也可以认为它不是指针)却给我8(指针的大小)。我的所有4个摘要之间是否存在实际差异?为什么它们的行为完全相同?
所有四个代码段都是相同的。
关于如何execute
调用,这在C11标准的6.3.2.1p4中涵盖:
函数指示符是具有函数类型的表达式。除非是运算
sizeof
符,_Alignof
运算符或一元运算&
符的操作数,否则将类型为“函数返回类型”的函数指定符转换为具有类型为“函数返回指针的指针”的表达式。
因此,因此调用execute(print)
或execute(&print)
都相同。
关于参数execute
,这将在6.7.6.3p8节中介绍:
将参数声明为“函数返回类型”声明调整为“指向函数返回类型的指针”,如6.3.2.1中所述
因此,这意味着void execute(void (*f)())
和void execute(void f())
相同。
我应该使用4个摘要中的哪一个,为什么?
这往往是样式问题,但是我个人将变量和参数声明为指向函数的指针类型,而不是函数类型,并且我将传递函数名而不使用address-of运算符。
我应该使用f()或(* f)()调用通过参数接收的传递函数
这也是一个风格问题。我会f()
比较容易阅读。
为什么在每个代码段的第一张图中,函数变量(sizeof(print))的大小始终为1?在这种情况下,我们实际得到的sizeof是多少?(显然不是指针的大小,在我的64位计算机上为8字节。如果使用sizeof(&print),我将获得指针的大小)
sizeof
根据第6.5.3.4p1节,明确禁止在功能指示符上使用:
的
sizeof
操作者不得应用于具有功能类型的表达式或不完整的类型,这样的类型的括号名称,或者将指定的位字段构件的表达式。的_Alignof
经营者不得被施加到函数类型或不完全类型。
这样做会引起未定义的行为。
为什么在代码片段1和3的第二张打印纸中,即使参数声明为void(f)(),sizeof(f)还是给我8(指针的大小)(所以如果没有*,我可以假定它不是指针)
这可以回溯到上面的6.7.6.3p8,即将function类型的函数参数转换为类型为function的指针,所以这就是您要得到的大小。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句