在C中传递函数或传递函数指针?

问题描述 投票:0回答:1

(注意:链接另一个显示函数指针示例的答案无济于事。我的问题恰恰是关于不同答案中显示的多种方式,并试图理解它们之间的差异)

上下文:

我试图了解将函数作为参数传递给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

我在我的Makefile中使用CFLAGS=-Wall -g -O0

我的代码示例

以下4个片段均产生相同的结果(至少具有相同的可见输出)。请注意,样本之间的唯一区别在于声明和execute函数的调用。我已经在引号中包括了我最初将这些示例中的每个示例称为只是为了区分它们的方式(因此命名可能是错误的)。

它们实际上只是以下四个排列的全部:

  • 声明功能execute以接收void f()void (*f)()
  • executeexecute(print)调用功能execute(&print)

请注意,在所有情况下,均使用f()而不是(*f)()调用该函数。 但是我也用(*f)()进行了测试,得出了相同的结果。所以8个排列,实际上(为简洁起见,这里仅显示4个)

  • 片段1:“传递without指针,接收without指针”

    #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:“传递with指针,并接收with指针”

    #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:“传递with指针,并接收without指针”

    #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:“传递without指针,并接收with指针”

    #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在第一次打印中始终为1
  • [sizeof在第二次打印中始终为8

我读过的东西

我已经阅读了几个示例(Wikipedia,StackOverflow,以及StackOverflow答案中链接的其他资源),其中很多显示了不同的示例。 我的提问正是为了理解那些差异

[Wikipedia article about function pointers显示了类似于代码段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)传递([无 &上的sin)]
  • 参数被声明为double (*funcp)(double)with *
  • 参数被调用为funcp(x)without *,因此没有(*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')传递(with &上的F
  • 参数被声明为Fn *fnwith *
  • 参数被调用为fn(c)without (*fn)

This answer

  • 参数作为func(print)传递([无 &上的print)]
  • 参数被声明为void (*f)(int)with *
  • 参数作为(*f)(ctr)调用(with (*fn)

This answer显示2个示例:

  • 第一个就像我的代码段1:
    • 参数作为execute(print)传递(without &
    • 参数被声明为void f() *
    • 参数被调用为f()without *
  • 第二个就像我的片段2:
    • 参数作为execute(&print)传递(with &
    • 参数被声明为void (*f)()with *
    • 参数被调用为f()without *

This answer

  • 没有关于如何传递函数参数(带有或不带有&的示例]
  • 参数被声明为int (*functionPtr)(int, int)with *
  • 参数作为(*functionPtr)(2, 3)调用(with *

This linked material I found in one of the answers(实际上是C ++,但是不使用任何C ++特定于函数指针的东西):

  • 参数作为&Minus传递(with &
  • 参数被声明为float (*pt2Func)(float, float)with *
  • 参数被调用为pt2Func(a, b)without *

我在这里提供了7个示例,这些示例在传递函数作为参数/接收函数作为参数/调用作为参数接收的函数时,提供了使用或不使用&*的至少5种组合。

我从这一切中学到的东西

[我相信上一节表明,关于StackOverflow和我链接的其他材料(大多数链接在StackOverflow的答案中)中最相关的问题/答案尚未达成共识。

似乎所有这四种方式都由编译器完全相同地处理,或者在如此简单的示例中没有出现非常细微的差异。

我理解为什么第二张打印在代码段2和4中将sizeof(f)打印为8。这就是我的64位系统中的指针大小)。但是我不明白为什么即使在execute函数声明不带*的参数的情况下,第二张打印也会打印指针的大小(代码段1和3),并且我也不明白为什么在第一张打印中函数变量的sizeof等于1

我的问题

  1. 我的4个摘要之间是否存在实际差异?为什么它们的行为完全相同?
    • 如果存在实际的运行时差异,您能解释一下其中每个发生了什么吗?
    • 如果没有运行时差异,您能否解释一下编译器在每种情况下所做的操作,以使它们的行为均相同(我假设编译器会看到f(myFunc)并实际使用f(&myFunc),或类似的东西。我想知道哪种是“规范”方式。
  2. 我应该使用四个摘要中的哪个,为什么?
  3. 我是否应该使用f()(*f)()调用通过参数接收的传递函数?
  4. 为什么在每个代码段的第一次打印中,功能变量(sizeof(print))的大小始终为1?在这种情况下,我们实际得到的sizeof是多少? (显然不是指针的大小,在我的64位计算机上为8字节。如果使用sizeof(&print),则将获得指针的大小。)>
  5. 为什么在代码段1和3上,在第二张打印中,即使参数被声明为sizeof(f)void (f)()也给我8(指针的大小)(因此,如果没有*,我可以假设它是不是指针)。
  6. ((请回答时,请尝试按编号的问题中断答案)

((注:链接显示函数指针示例的另一个答案是没有帮助的。我的问题恰恰是关于不同答案中显示的多种方式,并试图理解...的问题]

c pointers function-pointers
1个回答
0
投票
我的4个摘要之间是否存在实际差异?为什么它们的行为都完全相同?

0
投票
从C标准(6.3.2.1左值,数组和函数指示符)
© www.soinside.com 2019 - 2024. All rights reserved.