只回调编程w.r.t'c',我们为什么要使用?

时间:2015-04-19 11:14:10

标签: c pointers function-pointers

关于w.r.t c的观点,因为我对C更加自在。

我不希望有一个例子说它是如何工作的......我期待的是为什么我们应该使用回调函数或者有人说它作为函数指针。

我也跟踪了很多博客和堆栈溢出,但对这些答案都不满意。

让我们说(我在这里建议一个场景,比如排序事物)我们应该使用回调函数,其中方法/函数将花费更多时间进行处理。

假设一个进程只有一个线程,并且程序正在进行排序,这将花费大量时间(让我们假设> 1分钟)。根据博客的巨大数量,我们应该使用函数指针。但它会如何有用?

我们如何只有一个程序计数器,我们将有一些时间从CPU处理这个过程,那么它将如何有用?

如果您认为其他一些例子可以解释函数指针的概念,请提供示例。

我看到一些身体暗示,如果你将使用函数指针,那么结果你可以稍后收集,但这听起来很尴尬!即使可能,这怎么样?从那里回来之后你怎么能从函数中收集东西?这个功能会被毁掉!!!

实时人们将此用于事件的任何变化,以便他们可以获得通知......(只需添加一个点)

我见过一些好的程序员使用这个函数指针,我很想知道为什么我会使用它,肯定有一些我在这里缺少的东西......

请回复,先谢谢。

1 个答案:

答案 0 :(得分:0)

由于你上次评论中仍有一点不确定性,也许一个具体的例子说明这些要点会有所帮助。让我们从一个简单的例子开始,该例子从命令行获取一个字符串作为用户输入(您也可以提示用户输入)。现在让我们假设我们想让用户选择告诉我们他们想要如何存储输入。出于这个例子的目的,我们可以说选项是(1)来正常存储字符串,这样它就可以在一条线上打印,(2)存储相反的也将在一行上打印的字符串,(3)在每个字符后面用换行符存储字符串,使其垂直打印,(4)以相反的方式存储字符串嵌入式换行符。

在解决此问题的常规方法中,您可能会编写switch语句或一系列else if语句,并将字符串传递给4个不同的例程来处理不同的情况。函数指针允许您以稍微不同的方式处理问题。不是4个不同的输入例程来处理每种情况,为什么不是1个输入例程,它接受函数指针作为它的参数,并根据作为参数传递的函数改变它处理字符串的方式。输入例程可以像复制字符串一样简单(以防止修改argv[1]等...)然后将字符串作为参数传递给指针所代表的函数:

/* make copy of original string, pass to callback function */
char *input (char *d, char *s, char *(*cbf)(char *))
{
    strcpy (d, s);
    return (*cbf) (d);
}

上面的input函数将目标字符串d,源字符串s作为参数,然后是指向函数cbf的指针(回调的缩写)功能)。让我们快速查看函数指针语法并消化它告诉我们的内容:

char *(*cbf)(char *)
  |      |     \
return   |    argument
type     |    list
         |
    function pointer
      name/label

在上面,名为cbf的函数指针的返回类型为char *,并且采用类型为char *的单个参数。 (注意:仅在funciton指针参数列表中指定类型,而不是类型参数名称 - 例如,没有char *str,只有char *)现在每次想要传递该类型的函数作为参数或在赋值中使用它时,可能看起来很多。它是。有一个简单的解决方案可以减少所需的输入。您可以使用typedef作为函数指针,类似于将typedef与结构一起使用的方法等。创建类型为typedef的{​​{1}}同样容易:

cbf

上面的funciton指针typedef创建了一个类型typedef char *(*cbf)(char *); ,可以在需要函数指针类型的地方使用cbf代替char *(*cbf)(char *)。使用typedef时,您无需指定返回类型参数列表,也无需将函数指针放在括号内。这会将原始函数声明减少为:

char *input (char *d, char *s, cbf fname)
{
    strcpy (d, s);
    return fname (d);
}

对函数使用typedef不仅简化了将函数作为参数传递,而且还简化了创建函数指针数组的过程。根据需要,可以使用函数数组来简化选择和传递给定数量的函数中的任何一个。对于我们的input函数,我们创建了一个函数指针数组,每个函数指针指向一个不同的函数,可以用来将输入字符串放在所需的格式中。例如,假设我们上面描述的4个函数都有这样的声明:

/* input processing callback functions */
char *hfwd (char *s);
char *hrev (char *s);
char *vfwd (char *s);
char *vrev (char *s);

注意:每个函数都匹配类型char *的指针定义,并接受char *类型的单个参数。使用我们的cbf typedef ,我们可以轻松创建一个名为fnames的函数指针数组,如下所示:

cbf fnames[] = { &hfwd, &hrev, &vfwd, &vrev };

然后可以像任何其他数组一样使用fnames数组,通过数组索引选择任何一个函数。 (例如fnames[0]是我们的函数hfwd)现在,我们可以从用户那里获取第二个输入,即数字,以选择输入字符串的格式。这提供了使用我们的任何一个回调函数的能力,只需将所需函数的数组索引作为命令行的第二个参数。例如,可以通过调用以下程序来指定任何一个函数:

./progname my_string 1      /* to process the input with the hrev */

现在授予此示例并不比重新格式化字符串更多,但是从函数指针语法的角度来看,收集数组中的函数指针,并将函数指针作为参数传递以扩展代码的功能,它涵盖了很多。请看下面的示例,如果您有任何疑问,请与我们联系。 (回想一下,在没有typedef的情况下,还包括了完整的函数指针语法,但是进行了评论,以便您可以比较/对比typedef使用)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAXS 128

/* typedef for function pointer */
typedef char *(*cbf)(char *);

/* simple string reverse function */
char *strrevstr (char *str);

/* input processing callback functions */
char *hfwd (char *s);
char *hrev (char *s);
char *vfwd (char *s);
char *vrev (char *s);

/* input function, pointer to function will determine behavior */
// char *input (char *d, char *s, char *(*cbf)(char *));
char *input (char *d, char *s, cbf fn);

int main (int argc, char **argv) {

    if (argc < 3 ) {
        fprintf (stderr, "error: insufficient input, usage: %s string int\n", argv[0]);
        return 1;
    }

    int idx = atoi(argv[2]);

    if (idx > 3 || idx < 0) {
        fprintf (stderr, "error: invalid input -- out of range, (0 !< %d !< 3)\n", idx);
        return 1;
    }

    cbf fnames[] = { &hfwd, &hrev, &vfwd, &vrev };
    // char *(*fnames[])(char *) = { &hfwd, &hrev, &vfwd, &vrev };
    char string[MAXS] = {0};

    input (string, argv[1], fnames[idx]);

    printf ("\nProcessed input ('%s' '%s'):\n\n%s\n\n", argv[1], argv[2], string);

    return 0;
}

/* strrevstr - reverse string, original is not preserved. */
char *strrevstr (char *str)
{
    if (!str) {
        printf ("%s() error: invalid string\n", __func__);
        return NULL;
    }

    char *begin = str;
    char *end = str + strlen (str) - 1;
    char tmp;

    while (end > begin)
    {
        tmp = *end;
        *end-- = *begin;
        *begin++ = tmp;
    }

    return str;
}

/* string unchanged - print horizontal */
char *hfwd (char *s)
{ return s; }

/* string reversed - print horizontal */
char *hrev (char *s)
{ return strrevstr (s); }

/* string unchanged - print vertical */
char *vfwd (char *s)
{
    char *p = s;
    static char buf[MAXS] = {0};
    char *b = buf;

    while (*p)
    {
        *b++ = *p++;
        *b++ = '\n';
    }

    *b = 0;

    b = buf;
    while (*b)
        *s++ = *b++;
    *b = 0;

    return buf;
}

/* string reversed - print vertical */
char *vrev (char *s)
{
    char *p = strrevstr (s);
    static char buf[MAXS] = {0};
    char *b = buf;

    while (*p)
    {
        *b++ = *p++;
        *b++ = '\n';
    }

    *b = 0;

    b = buf;
    while (*b)
        *s++ = *b++;
    *b = 0;

    return buf;
}

/* make copy of original string, pass to callback function */
char *input (char *d, char *s, cbf fn)
// char *input (char *d, char *s, char *(*cbf)(char *))
{
    strcpy (d, s);
    return fn (d);
//     return (*cbf) (d);
}

<强>输出

$ ( for i in {0..3}; do ./bin/fnc_pointer my_string $i; done )

Processed input ('my_string' '0'):

my_string


Processed input ('my_string' '1'):

gnirts_ym


Processed input ('my_string' '2'):

m
y
_
s
t
r
i
n
g


Processed input ('my_string' '3'):

g
n
i
r
t
s
_
y
m