【问题标题】:Returning a pointer to a function in C syntax在 C 语法中返回指向函数的指针
【发布时间】:2016-11-10 23:31:01
【问题描述】:

How do function pointers in C work? 的答案之一中,一位用户解释了如何在返回值中使用函数指针。这是有问题的代码:

// this is a function called functionFactory which receives parameter n
// and returns a pointer to another function which receives two ints
// and it returns another int

int (*functionFactory(int n))(int, int) {
    printf("Got parameter %d", n);
    int (*functionPtr)(int,int) = &addInt;
    return functionPtr;
}

对我来说,你声明 functionFactory 函数的方式很奇怪——在我看来你混淆了返回类型(指向函数的指针)和函数名本身(functionFactory)。

例如,当我们编写一个返回其参数平方的简单函数时,我们会编写类似

int square(int n){
    return n*n;
}

很明显,我们返回的类型在左边,然后我们写函数名,然后它接受什么参数。所以当我们返回一个指向函数的指针时,我们为什么不写这样的东西:

( int (*function)(int, int) ) functionFactory(int n) { ...

这里的返回类型(它是一个函数的指针)和它的细节(例如我们指向的函数返回什么以及它接受什么作为参数)在左边清楚地分开并且functionFactory函数本身的名称是在右边。对我来说,我的版本似乎更合乎逻辑和清晰,我们为什么不这样写呢?

【问题讨论】:

  • C 对声明符使用中缀表示法。这是根据“声明遵循使用”的原则。我们以a[5] 访问数组,因此它被声明为int a[5];,而不是int[5] a;。函数类型也是如此。
  • int (*function)(int, int) functionFactory(int n) { ... "这里是返回类型(这是一个指向函数的指针)" - 如果不是返回类型,那么该行中的第一个 int 是什么?
  • 您当然可以为这些函数指针声明 typedef,然后使用这些 typedef 的函数声明看起来更“正常”。

标签: c pointers syntax


【解决方案1】:

这不符合声明符语法的工作方式。从替代的角度考虑可能会有所帮助。这是一种查看方式:

T    f   (); // f is a function returning T
     |
     v
T  (*p)  (); // p is a pointer to a function returning T
     |
     v
T  (*q())(); // q is a function returning a pointer to a function returning T

所以我们从函数声明符f() 开始。然后我们用指针(*p) 替换f,得到声明符(*p)()。最后我们用函数q()替换p,给我们声明符(*q())()

编辑

在阅读毛茸茸的声明符时,您可能会听到人们谈论“螺旋规则”。虽然它更像是一个指导而不是实际规则,但它不属于以下优先规则:

T *a[N];    // a is an array of pointer to T
T (*a)[N];  // a is a pointer to an array of T
T *f();     // f is a function returning T
T (*f)();   // f is a pointer to a function returning T

后缀[]() 运算符的优先级高于一元*,因此在声明指向数组或函数的指针时需要括号。所以当我们读到T (*q())();时,我们从最左边的标识符q开始,向外“螺旋”:

    +-----------+
    | +-------+ |
    | | +---+ | |         
    | | |   | | |
    T ( * q ()) ()
      | | | | | |
      | | +-+ | |
      | +-----+ |    
      +---------+

逐段分解:

    q        -- q is a
    q()      -- function returning
   *q()      -- pointer to
  (*q())()   -- function returning
T (*q())();  -- T

不能声明函数类型的数组,也不能声明返回数组类型的函数:

T a[N]();    // NOT ALLOWED 
T f()[N];    // NOT ALLOWED 

所以在函数和数组类型的任何组合中,总是会涉及到一个指针,所以螺旋规则成立。

【讨论】:

    【解决方案2】:

    以下代码将名为 func 的变量声明为指向返回 int 并将两个 ints 作为参数的函数的指针:

    int (*func)(int, int);
    

    这种语法模仿了在 C 中声明函数的语法:

    int func(int, int);
    

    看看这有多相似?实际上,C 中的函数就像指向函数的变量,例如请参阅此示例:

    int funcA(int a, int b) { ... }
    int (*funcB)(int, int) = funcA;
    

    如果您调用funcA(a,b)funcB(a,b),它在其余代码中不起作用,是吗?所以即使funcB是一个变量,funcA是一个函数,但事实上,内存中的某处有一个函数,funcAfuncB都是指向函数代码地址的指针。

    如果你想挑剔,funcA 是一个常量,funcB 是一个变量,所以调用funcA 的编译器输出将不同于funcB;在一种情况下,它是直接调用,在一种情况下是间接调用,因为funcB 的目的地会随着时间而改变。这也是星号所说的,“我不是函数,我是指向函数的指针”。

    请务必注意,在 C 语言中,星号始终位于变量名旁边,以标记指针变量,那么您还想怎么写呢?像这样?

    // Careful! Wrong!
    int (int, int) *func;
    

    这对您来说看起来更具可读性吗?或者可能是这样?

    // Careful! Wrong!
    int ()(int, int) *func;
    

    这种语法也不是很清楚,也没有模仿任何其他现有的 C 语法。如果你经常使用函数指针,传递它们并将它们存储在变量中,你应该为你的函数指针声明一个自己的类型:

    #include  <stdio.h>
    
    int sum ( int a, int b ) {
        return (a + b);
    }
    
    typedef int (* MathFuncType)(int, int);
    
    MathFuncType getSumFunc ( ) {
        return &sum;
    }
    
    int main(void) {
        MathFuncType func = getSumFunc();
        printf("%d\n", func(3, 8));
        return 0;
    }
    

    http://rextester.com/GEKR20461

    【讨论】:

      猜你喜欢
      • 2012-02-02
      • 1970-01-01
      • 2020-04-29
      • 2013-04-10
      • 2012-06-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-29
      • 1970-01-01
      相关资源
      最近更新 更多