【问题标题】:What does int (*f[])(int*) represent?int (*f[])(int*) 代表什么?
【发布时间】:2015-08-22 12:46:11
【问题描述】:

这是考试题之一,目的是确定程序会写什么。我真的很困惑int (*f[])(int*) = {f1, f2, f2, f1 }; 是什么。我教过它可能是一个数组,其元素是括号中函数的结果,而这些结果是指向 int 的地址。

你能解释一下吗?

还有,函数f[i++]得到什么参数

for (i=0; i<2; a += fx(f[i++], a));

问题:

int f2(int *p) { return *p-- + 2; }

int f1(int *q) { return ++*q; }

int fx(int (*pf)(int*), int q) {
    int t = q;
    while ((t = pf(&q)) % 2) q+=2;
    return t;
}

#include <stdio.h>

void main() {
    int a = 4, b = 3, *p = &b, i;
    int (*f[])(int*) = {f1, f2, f2, f1 };

    for (i=0; i<2; a += **fx(f[i++]**, a));
    printf("%d", a + *p);
}

【问题讨论】:

  • 缩进!缩进!缩进!

标签: c


【解决方案1】:

申请clockwise/spiral rule

int (*f[])(int*)
      ^                f is

int (*f[])(int*)
      ^^^              f is an array

int (*f[])(int*)
    ^^^^^^             f is an array of pointers

int (*f[])(int*)
    ^^^^^^^....^       f is an array of pointers to functions


int (*f[])(int*)
    ^^^^^^^^^^^^       f is an array of pointers to functions
                       that accept a pointer to int

int (*f[])(int*)
^^^^^^^^^^^^^^^^       f is an array of pointers to functions
                       that accept a pointer to int and return a int value

【讨论】:

  • 非常感谢!只是另一件事。为什么 int (f[])(int) 不写成 int (int*)(*f[])?我的意思是,这对我来说更有意义。可以这样写吗?
  • 嗯 .... C 被设计成 “声明遵循用法”。您使用语法为obj = fx(parm1, parm2, ...); 的函数,并且声明指向函数的指针的方式遵循该结构。
  • 解释事物的最佳方式! :D
【解决方案2】:

它将f 声明为指向函数的指针数组,该函数返回int 并将int * 作为参数。

Expert C Programming: Deep C Secrets一书中讨论了理解复杂声明的优先规则:

理解 C 声明的优先规则

  • A.声明以名称开头,然后按优先顺序读取。

  • B.优先级从高到低依次为:

  • B.1。括号将声明的各个部分组合在一起

  • B.2。后缀运算符:
    括号()表示函数,和
    方括号[] 表示一个数组。

  • B.3。前缀运算符:
    表示“指向”的星号。

  • C.如果const 和/或volatile 关键字紧挨着类型说明符(例如intlongetc。)它适用于类型说明符。否则,const 和/或volatile 关键字适用于其紧靠左侧的指针星号。

因此,它是这样的:

      f                          -- f (A)
     f[]                         -- is an array (B.1)
    *f[]                         -- of pointers to (B.3)
   (*f[])( )                     -- function (B.2)
 (*f[])( int * )                 -- that expects a pointer to an int as an argument
int (*f[])( int* )               -- and return an int     

我建议在某些情况下避免螺旋规则,因为它fails,例如int* a[10][15];

【讨论】:

    【解决方案3】:

    让我们在很棒的C gibberish to English website 的帮助下把它拆开:

    int (*f[])(int*)
    

    将 f 声明为返回 int 的函数指针数组(指向 int 的指针)

    f 是一个函数指针数组。

    因此,使用{} 数组初始化器将函数存储在其中非常清楚。

    【讨论】:

      【解决方案4】:

      与通常的教学方法不同,我建议你从内开始。认清声明的一般结构并细化:将你看到的粗粒度“斑点”替换为更多详细的。换句话说,从语法树的根到叶子遍历语法,而不是试图挑出正确的叶子并自下而上工作。

      声明的一般结构(只有一个声明符)是:

      TYPE WHATEVER;
      

      WHATEVER 被声明,与TYPE 相关。

      现在注意TYPEint,所以WHATEVERint 类型。而WHATEVER 的一般形式为(W1)(W2):括号中的两个句法单元,无论是1 还是2:

      int (W1)(W2);
      

      这里,W1 是被声明的内容,它是一个返回int 的函数,它接受W2 参数列表。后者实际上表示int *

      int (W1)(int *);
      

      因此W1 是一个返回int 的函数,它接受int *。但W1 实际上是*W3,这使得W3 成为指向W1 类型的指针。

      int (*W3)(int *);
      

      W3 是一个指向返回 int 的函数的指针,该函数采用 int *

      W3 实际上是f[],所以fW3 类型的未指定大小的数组:

      int (*f[])(int *);
      

      f 是一个未指定大小的指针数组,指向返回 int 的函数,它采用 int *

      提示:我们怎么知道W1 实际上不是W3[]W3*f?这是因为类型构造运算符[...] 在语法上类似于后缀数组索引运算符[...],而类型构造* 类似于解引用一元运算符*。一元运算符的优先级低于后缀运算符。当我们看到*X[] 时,我们知道这意味着*(X[]) 而不是(*X)[]。符号*X 不构成该短语中的句法单元。如果我们想要第二个含义,我们必须使用括号。

      语法树是:

                                 declaration
                                  |      |
                     +------------+      +----------------+
                     |                                    |
       specifier-qualifier list -- TYPE              declarator -- WHATEVER
                     |                                |       |
                    int                         +-----+       |
                                                |             |
                                           function -- W1    params -- W2
                                                |               |
                                             pointer -- W3    (int *)
                                                |
                                              array -- f
      

      W3 这样有用的符号只是我们从根遍历到叶子时节点的标签。在 C 声明中声明的名称位于树的底部,与其他一些语言不同。随着深入进入树,我们实际上正在走出类型,所以当我们最终到达底部时,我们知道最重要的事情:f 是被声明,总的来说它是一个数组。

      【讨论】:

        【解决方案5】:

        the clockwise/spiral rule 之后,我们将看到f 是一个指向函数的数组,其中数组中的每个函数都接受一个int* 参数并返回一个int

        初始化只是用一些函数指针初始化这个数组。

        知道f 是什么应该有助于解决其他问题。

        【讨论】:

          【解决方案6】:

          这个

          int (*f[])(int*) = {f1, f2, f2, f1 }; 
          

          是一个int(int *)类型的函数指针数组的声明,该数组是具有返回类型int和一个int *类型参数的函数。

          由于f 是一个数组,那么f[i++] 是数组中的一个元素,具有f1、f2 等值之一。

          这个函数

          int fx(int (*pf)(int*), int q); 
          

          有两个参数:一个指向 int(int *) 类型的函数的指针和一个 int 类型的对象。

          所以这个表达式

          fx(f[i++], a)
          

          是函数的调用,它接受函数指针f1、f2等和对象a作为参数之一

          【讨论】:

            【解决方案7】:

            它本质上定义了一个函数指针数组。以下:

            int (*f[])(int*) = {f1, f2, f2, f1};
            

            将定义与数组初始值设定项结合起来。这就是为什么你可以省略数组的大小,因为它是从初始化程序推导出来的。每个元素的类型为:

            int (*)(int*)
            

            这是函数的函数指针,它接受一个int * 类型的参数并返回int

            for 循环的主体由空语句组成。您可以将其重写为:

            for (i = 0 ; i < 2; a += fx(f[i++], a))
                ;
            

            i = 0i = 1 有两次迭代。每次a 由函数调用的结果递增:

            fx(f[i++], a)
            

            第一个参数是函数的地址,存储在f数组中。分别是f[0]f[1](即f1f2)。您可以使用等价形式:

            fx(&f[i++], a)
            

            但不会有真正的区别(这只是偏好问题)。

            如果它看起来对你来说太奇怪或太复杂,那么你可以将 for 循环重写为:

            for (i = 0 ; i < 2; a += fx(f[i], a), i++)
                ;
            

            还有:

            for (i = 0 ; i < 2; i++)
                a += fx(f[i], a);
            

            【讨论】:

              猜你喜欢
              • 2011-01-11
              • 2011-02-27
              • 2013-01-26
              • 1970-01-01
              • 2012-12-08
              • 1970-01-01
              • 2015-08-10
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多