【问题标题】:What does this typedef declaration mean?这个 typedef 声明是什么意思?
【发布时间】:2021-03-10 03:26:42
【问题描述】:

我知道 C 语言中的 typedef,但是当我在 K&R 书中看到声明时,我感到很困惑。声明是

typedef int (*PFI) (char *,char *)

这是什么意思?什么是PFI,我们如何在程序中使用它?

【问题讨论】:

  • 阅读函数指针。
  • PFI 是一个指向函数的指针,该函数接受两个char * 参数并返回一个int。
  • 我认为您在这 15 分钟内没有阅读有关函数指针的内容。
  • 两位作者非常彻底地讨论了函数指针。在第 6.7 章中,他们介绍了typedef 并给出了这个例子,他们说在同一个句子中,这个声明'创建了 PFI 类型,因为“指向函数的指针(两个 char * 参数)返回 int, " [这回答了你的第一个问题] 可以在第 5 章的排序程序中的PFI strcmp, numcmp; 等上下文中使用——这是他们介绍和讨论函数指针的地方,回答了你的第二个问题。你错过了这一切吗?

标签: c typedef kernighan-and-ritchie


【解决方案1】:

分解一下,PFI 是一个指针:

*PFI

到一个函数:

(*PFI)()

这需要两个char * 类型的参数:

(*PFI)(char *, char *)

并返回int:

int (*PFI)(char *, char *)

并声明为该类型的别名:

typedef int (*PFI)(char *, char *);

如果你有一个匹配这个指针类型的函数,你可以使用它,即:

int foo(char *x, char *y)
{
    ...
}

然后您可以将指向函数的指针分配给PFI类型的变量:

PFI *f = &foo;

【讨论】:

    【解决方案2】:

    您在阅读有关指向函数的指针吗?

    如果是,那么 PFI 只是一个占位符。它可以是你给的任何名字,它只是一个函数名。我猜它可能意味着“首选函数接口”,但这只是一个猜测。

    假设你写了一个非常粗略的代码

    typedef bool (*write_X_Bytes) (uint32 count, char *buffer);
    

    那么你可以将它用作 C 中的类型,因为你在它前面使用了 typedef,就像

    write_X_Bytes print_line, print_word;
    

    基本上你给用户或任何想要使用这个关于参数和返回的代码的人提供一个原型。您可以查看 GPIO 的 Linux 内核驱动程序代码以用于实际用例。

    【讨论】:

      【解决方案3】:

      指针声明的基本规则:

      T *p;        // p is a pointer to T
      T *ap[N];    // ap is an N-element array of pointers to T
      T *fp();     // fp is a function returning a pointer to T
      
      T (*pa)[N];  // pa is a pointer to an array of T
      T (*pf)();   // pf is a pointer to a function returning T
      

      在表达式和声明符中,下标 [] 和函数调用 () 运算符的优先级高于一元解引用 * 运算符,因此像 *f() 这样的表达式或声明符被解析为 *(f()) - 你正在取消引用 f 的结果。如果你想在调用它之前取消引用f ,你必须明确地将* 运算符与它组合在一起 - (*f)()。

      阅读毛茸茸的声明的方法是从最左边的标识符开始,按照上面的规则,递归地应用到任何函数参数。

      如果你有一个像这样在参数列表中使用抽象声明符的函数,请记住:

      T *       -> T *λ
      T *[N]    -> T *λ[N] 
      T (*)[N]  -> T (*λ)[N]
      T *()     -> T *λ()
      T (*)()   -> T (*λ)()
      

      其中λ 表示标识符通常的位置。

      所以,阅读这个声明的方式是

                    PFI                     -- PFI
      typedef       PFI                     -- is a typedef name for 
      typedef      *PFI                     -- pointer to
      typedef     (*PFI) (             )    --   a function taking
      typedef     (*PFI) (             )    --     unnamed parameter
      typedef     (*PFI) (     *       )    --       is a pointer to
      typedef     (*PFI) (char *       )    --         char
      typedef     (*PFI) (char *,      )    --     unnamed parameter
      typedef     (*PFI) (char *,     *)    --       is a pointer to
      typedef     (*PFI) (char *,char *)    --         char
      typedef int (*PFI) (char *,char *);   --   returning int
      

      在不太正式的术语中,PFI 是“指向带有两个 char * 参数并返回 int 的函数的指针”类型的别名。

      那么我们将如何使用它呢?

      一个常见的用途是在函数参数列表中声明回调:

      void foo( char *foo, char *bar, PFI callback )
      {
        int x = callback( foo, bar );
        ...
      }
      

      这比读起来容易一点

      void foo( char *foo, char *bar, int (*callback)(char *, char *) )
      {
        int x = callback( foo, bar );
        ...
      }
      

      它对于声明数组或struct 成员也很有用:

      int func1( char *x, char *y ) { ... }
      int func2( char *a, char *b ) { ... }
      ...
      
      struct s {
        char *x;
        char *y;
        PFI f;
      } blah = {"foo", "bar", func1};
      
      PFI callbacks[] = { func1, func2, ... };
      

      再一次,这比写作要干净一些

      struct s {
        char *x;
        char *y;
        int (*f)(char *, char *);
      };
      

      或

      int (*callbacks[])(char *, char *) = { func1, func2, ... };
      

      但是……

      “稍微干净一点”本身并不是将类型隐藏在typedef 后面的充分理由。您使用typedef 抽象出类型的用户不需要知道才能使用它的类型的详细信息。

      例如,考虑stdio.h 中的FILE 类型。有一个完整的 API 支持 FILE 类型,因此您永远不需要知道它的底层是什么样子。

      然而,在我们的例子中,我们必须知道PFI 是一个指向函数的指针,该函数需要两个char * 参数并返回一个int 才能使用它。使用 typedef 名称节省了一些击键,但它也创建了一个“泄漏”的抽象,因为有关类型的详细信息必须“泄漏”给程序员。在这种情况下,老实说,最好完全放弃 typedef 并使用“裸”声明。

      【讨论】:

        【解决方案4】:

        https://cdecl.org/ 表示“将 PFI 声明为指向函数的指针(指向 char 的指针,指向 char 的指针)返回 int”。该网站不喜欢最初的 typedef,但这使它成为一种类型,因此您可以编写:

        PFI fp = f;

        f 会有原型:

        int f(char *, char *);

        【讨论】:

          猜你喜欢
          • 2014-09-20
          • 2022-12-05
          • 1970-01-01
          • 1970-01-01
          • 2022-07-20
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多