【问题标题】:A Question About Function Pointer In CC语言中关于函数指针的一个问题
【发布时间】:2009-04-16 15:01:27
【问题描述】:

有以下声明:

void qsort(void *lineptr[], int left, int right, int (*comp)(void *, void *));
int numcmp(char *, char *);
int strcmp(char *s, char *t);

然后,在程序的某处有如下调用:

  qsort((void**) lineptr, 0, nlines-1, 
                    (int (*)(void*,void*))(numeric ? numcmp : strcmp));

(忽略前三个参数和numeric)。

我问这是什么:

(int (*)(void*,void*))(numeric ? numcmp : strcmp)

我知道qsort 期待一个“指向函数的指针,该函数获取两个void 指针并返回一个int”,因为它是第四个参数,但上面写的内容如何满足这一点? 在我看来,这像是某种演员表,因为它由两个括号组成,但那将是一个非常奇怪的演员表。因为它需要一个函数并使该函数成为“指向函数的指针,该函数获取两个void 指针并返回一个int”。这是没有意义的。
(我在这里遵循了一个规则,即在变量之前括号中的类型 type 将变量提升为该类型)。

所以我想我弄错了,也许有人可以告诉我如何阅读,顺序是什么?

【问题讨论】:

  • 为什么没有意义?对我来说看起来还不错。
  • 因为你拿了一个函数,让它没用。它变成了一个指针。它不再是一个功能。此外,传递给 qsort 的是什么?只是一个应该指向函数的指针,但没有人告诉它指向什么函数。我也认为将 func' 转换为指针正在缩小
  • 指向函数的指针是函数指针...完全可以作为函数使用,谢谢...

标签: c pointers casting function-pointers


【解决方案1】:

这里发生的事情确实是一个演员表。让我们暂时忽略三进制并假设始终使用 numcmp。出于这个问题的目的,函数可以充当 C 中的函数指针。因此,如果您查看 numeric 的类型,它实际上是

(int (*)(int*,int*))

为了在 qsort 中正确使用它,它需要有 void 参数。因为这里的类型在参数和返回类型方面都具有相同的大小,所以可以用 on 代替另一个。所需要的只是使编译器满意的强制转换。

(int (*)(void*,void*))(numcmp )

【讨论】:

  • 你要说的是numcmp,不是numeric
  • 您写道“它实际上是数字的类型 (int ()(int,int*))”。我不明白。三元表达式的类型是一个函数,你写的是一个指向函数的指针。
  • @Leif,函数和指向函数的指针在 C 中是有点等价的。就这个问题而言,它们可以被认为是相同的。
  • 函数会自动衰减为函数指针,就像数组衰减为指向其第一个元素的指针一样。
  • 你的意思是当传递给我假设的其他函数时,函数会衰减为指向函数的指针?
【解决方案2】:

你错过了这里的诀窍 - 部分

(numeric ? numcmp : strcmp)

正在使用三元运算符来选择 在 qsort 中调用哪个函数。如果数据是数字,则使用 numcmp。如果没有,它使用 strcmp。一个更具可读性的实现如下所示:

int (*comparison_function)(void*,void*) = 
    (int (*)(void*,void*))(numeric ? numcmp : strcmp);
qsort((void**) lineptr, 0, nlines-1, comparison_function);

【讨论】:

  • @Sean Bright:我真的不认为 一种方法可以使其可读。我在其中看到的唯一优点是它使用了一个变量名(comparison_function)来试图解释发生了什么。
  • 你的语法是错误的,尽管函数指针的语法是非常尴尬和混乱的。
  • @Adam:请随意修复它 - 我比 C 更 C++,而且正如你提到的,函数指针有点毛茸茸。
  • @Harper:这并不是真的对你开枪。如果没有 typedef 或大量空白,就没有优雅的清理方法。
  • 当然,有一种更易读的方法...typedefs!。 typedef int (compare_t)(void *, void *);那么你可以这样做: qsort((void*) lineptr, 0, nlines-1, (compare_t)(numeric ? numcmp : strcmp));
【解决方案3】:

正如其他人指出的那样,对于

(int (*)(void*,void*))(numeric ? numcmp : strcmp)

那么下面是类型转换

(int (*)(void*,void*))

表达式是

(numeric ? numcmp : strcmp)

C 声明可能很难阅读,但可以学习。方法是从内部开始,然后向右走一步,然后向左走一步,向外继续向右,向左,向右,向左等直到完成。在对括号内的所有内容进行评估之前,您不会越过括号外。例如对于上面的类型转换,(*) 表示这是一个指针。指针是括号内唯一的东西,所以我们评估它外面的右侧。 (void*,void*) 表示它是一个指向具有两个指针参数的函数的指针。最后int 表示函数的返回类型。外括号使其成为类型转换。 更新:两篇更详细的文章:The Clockwise/Spiral RuleReading C Declarations: A Guide for the Mystified

不过,好消息是,虽然上面的知识非常有用,还有一个非常简单的作弊方法cdecl 程序可以从 C 转换为英文说明,反之亦然:

cdecl> explain (int (*)(void*,void*))
cast unknown_name into pointer to function (pointer to void, pointer to void) returning int
cdecl> declare my_var as array 5 of pointer to int
int *my_var[5]
cdecl>

练习:i是什么变量?

int *(*(*i)[])(int *)

回答rot13,以防你的机器上没有安装cdecl(但你真的应该!):

pqrpy> rkcynva vag *(*(*v)[])(vag *)
qrpyner v nf cbvagre gb neenl bs cbvagre gb shapgvba (cbvagre gb vag) ergheavat cbvagre gb vag
pqrpy>

【讨论】:

    【解决方案4】:

    你可以在没有函数指针转换的情况下做到这一点。这是how。以我的经验,在大多数地方,如果你使用演员表,你就做错了。

    【讨论】:

      【解决方案5】:

      注意qsort()的标准定义包括const

      void qsort(void *base, size_t nmemb, size_t size,
                 int (*compar)(const void *, const void *));
      

      请注意,字符串比较器有两个“char **”值,而不是“char *”值。

      我编写比较器以便在调用代码中不需要强制转换:

      #include <stdlib.h>    /* qsort() */
      #include <string.h>    /* strcmp() */
      
      int num_cmp(const void *v1, const void *v2)
      {
          int i1 = *(const int *)v1;
          int i2 = *(const int *)v2;
          if (i1 < i2)
              return -1;
          else if (i1 > i2)
              return +1;
          else
              return 0;
      }
      
      int str_cmp(const void *v1, const void *v2)
      {
          const char *s1 = *(const char **)v1;
          const char *s2 = *(const char **)v2;
          return(strcmp(s1, s2));
      }
      

      强迫人们使用你的函数在代码中编写强制转换是丑陋的。不要。

      我写的两个函数符合标准qsort()所要求的函数原型。不带括号的函数名等价于指向函数的指针。

      您会发现在较旧的代码中,或由那些在较旧的编译器上长大的人编写的代码中,使用以下符号来使用指向函数的指针:

      result = (*pointer_to_function)(arg1, arg2, ...);
      

      用现代风格写成:

      result = pointer_to_function(arg1, arg2, ...);
      

      就个人而言,我发现显式取消引用更清晰,但并非所有人都同意。

      【讨论】:

        【解决方案6】:

        编写该代码的人 sn-p 太聪明了。在他的脑海中,他可能认为通过制作一个聪明的“单线”,他正在成为一名优秀的程序员。 实际上,他编写的代码可读性较差,长期使用起来令人讨厌,应该以更明显的形式重写,类似于 Harper Shelby 的代码。

        记住 Brian Kernighan 的格言:

        调试是写作的两倍 首先是代码。 因此,如果您将代码编写为 尽可能聪明地,你是,通过 定义,不够聪明,无法调试 它。


        我在严格的实时期限内进行了许多性能关键的编码......我仍然没有看到适合密集单行的地方。

        我什至弄乱了编译和检查 asm 以查看 one-liner 是否具有更好的编译 asm 实现,但从未发现 one-liner 值得。

        【讨论】:

        • 我忘了说这是来自 K&R 的代码片段,所以也许 Kernighan 在那里自相矛盾。
        • 现在把嘴伸进脚...
        【解决方案7】:

        我可能会这样读:

        typedef int (*PFNCMP)(void *, void *);
        
        PFNCMP comparison_function;
        
        if (numeric)
        {
            comparison_function =  numcmp;
        }
        else
        {
            comparison_function = strcmp;
        }
        
        qsort((void**) lineptr, 0, nlines-1, comparison_function);
        

        问题中的示例有一个明确的案例。

        【讨论】:

          【解决方案8】:

          我认为你的逻辑是正确的。它确实是转换为“指向函数的指针,该函数获取两个 void 指针并返回一个 int”,这是方法签名所需的类型。

          【讨论】:

            【解决方案9】:

            numcmpstrcmp 都是指向以两个char* 作为参数并返回int 的函数的指针。 qsort 例程需要一个指向函数的指针,该函数接受两个void* 作为参数并返回一个int。因此演员阵容。这是安全的,因为void* 充当通用指针。现在,继续阅读声明:让我们以 您的 strcmp 的声明:

             int strcmp(char *, char *);
            

            编译器将其读取为strcmp实际上是:

             int (strcmp)(char *, char *)
            

            一个接受两个char * 参数的函数(在大多数情况下衰减为指向函数的指针)。因此指针strcmp 的类型为:

             int (*)(char *, char *)
            

            因此,当您需要将另一个函数转换为与strcmp 兼容时,您将使用上述作为要转换的类型

            同样,由于qsort 的比较器参数需要两个void *s,因此是奇数演员!

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2021-07-14
              • 2021-10-29
              • 1970-01-01
              • 2013-03-11
              • 1970-01-01
              • 2014-05-13
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多