【问题标题】:In C why can I pass function name (not a pointer) as parameter to funtion? [duplicate]在 C 中,为什么我可以将函数名(不是指针)作为参数传递给函数? [复制]
【发布时间】:2017-08-24 02:34:50
【问题描述】:

我已经测试了以下代码:

#include <stdio.h>
void f(void g()) {
    g();
}

void g(void) {
    printf("hello, world\n");
}

int main() {
    f(g);
    return 0;
}

效果很好。在上面的代码中,为什么在c中我们可以将函数名g作为参数,并将函数原型作为参数?

在 K&R 书中,它说我们可以使用函数指针: 所以我可以像这样定义函数f

void f(void (*g)()) {
    g();
}
void g(void) {
    printf("hello, world\n");
}

int main() {
    void (*pf)() = g;
    f(pf);
    f(g);   // Here f(g) also works.
    return 0;
}

所以在第一个f定义形式中,参数原型是void g(),在第二个形式中参数原型是void (*g)()。为什么f的两种定义形式是一样的?

而在第二种形式中,参数是指向函数的指针,为什么f(g)也有效?

【问题讨论】:

  • 在大多数情况下,函数名被转换为函数指针。
  • 也就是说,我们在大多数情况下都可以使用函数名作为指针,那么我可以说不需要定义指向函数的指针吗?然后我可以直接使用函数名作为其他语言。
  • @zhenguoli:就像void f(int a[]),其中a实际上是int *类型,而不是int[]
  • 不,你不能这么说。
  • @n.m.为什么?指向函数的任何原因或优势?

标签: c function function-pointers standards


【解决方案1】:

来自 C 标准(6.3.2.1 左值、数组和函数指示符)

4 函数指示符是具有函数类型的表达式。 除非它是 sizeof operator65) 的操作数或一元 & 运算符,类型为“函数返回类型”的函数指示符 转换为具有“函数指针”类型的表达式 返回类型''

和(6.7.6.3 函数声明符(包括原型))

8 将参数声明为“函数返回类型”应为 调整为“指向函数返回类型的指针”,如 6.3.2.1。

因此这些函数声明

void f(void g());

void f(void ( *g )());

声明同一个函数。

【讨论】:

    【解决方案2】:

    因为函数在函数调用上下文中不使用时会衰减为函数指针:

    void f(void a()) { a(); }    //void a() decays into void (*a)()
    void g(void (*a)()) { a(); }    //equivalent to the above
    void h(void a()) { (*a)(); }    //*a is equivalent to a
    void i(void a()) { (**a)(); }    //**a is equivalent to *a
    void j(void a()) { (***a)(); }    //...
    void k() { };
    
    int main() {
        f(k);
        g(k);
        h(k);
        i(k);
        j(k);
    }
    

    尽管我一直在参数函数上使用大量的解引用运算符,但它编译并运行良好。每次我使用它时,结果函数在遇到下一个取消引用运算符时都会衰减回指针。同样,函数参数void a() 衰减为指针,就像int array[] 用作函数参数时衰减为int* array

    【讨论】:

      【解决方案3】:

      当用作函数参数时,函数名表现为指向函数本身的指针。
      这些都是有效的原型

      void f(void (g)());
      void f(void (*g)());
      void f(void (**g)());
      void f(void (*****g)());
      

      当您声明一个函数指针时,您可以使用该指针作为调用

      g();
      (*g)();
      (*****g)();
      

      【讨论】:

      • 就我而言,也许没有必要定义指向函数的指针。有什么材料可以解释这个标准吗?我在读K&R,还没有谈过。
      • @zhenguoli 如果你想要一个可以改变的变量,你必须使用函数指针。
      【解决方案4】:

      C 标准 ISO/IEC 9899:2011,第 6.3.2.1 节第 4 项说:

      函数指示符是具有函数类型的表达式。除非它是 sizeof 运算符、_Alignof 运算符、65) 或一元 @987654323 的操作数@ 运算符,类型为“函数返回类型”的函数指示符被转换为类型为“指向函数返回类型的指针”的表达式。

      所以通常函数名(或具有函数类型的其他表达式,即函数指示符)会转换为指向函数的指针,但上述情况除外。

      那么当您将&amp; 运算符应用于函数时会发生什么,因为这是上述条款中排除的情况之一。答案由 6.5.3.3 节第 3 项给出,相关部分为:

      一元 &amp; 运算符产生其操作数的地址。如果操作数的类型为“type”,则结果的类型为“pointer to type”。 [...] 否则,结果是指向其操作数指定的对象或函数的指针。

      (我错过的“[...]”部分讨论了将&amp; 运算符应用于一元* 运算符的结果和[] 运算符的结果。)

      因此,将&amp; 应用于函数名称(或其他函数指示符)也会为您提供指向函数的指针。

      那么,当您使用一元 * 运算符取消引用函数指针时会发生什么?它产生一个函数指示符(第 6.5.3.3 节第 4 项),在大多数情况下(如上所述)它会被转换回指向函数的指针!

      编辑:我忽略了将函数的参数声明为返回类型的函数的情况。正如弗拉德在莫斯科的回答中提到的第 6.7.6.3 节第 8 项所讨论的那样,它会自动调整为指向返回 type 的函数的指针。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-04-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多