了解在C函数指针的typedef [英] Understanding typedefs for function pointers in C

查看:107
本文介绍了了解在C函数指针的typedef的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直有点当我看到其他人的code其中有针对函数指针与参数类型定义难住了。我记得我花了一段时间来避开这样的定义,而试图理解一个数值算法用C写的前一阵子。所以,你可以分享你的技巧和想法如何编写良好的指针类型定义为函数(做的和不要的),至于他们为什么有用,如何理解别人的工作?谢谢!

I have always been a bit stumped when I read other peoples' code which had typedefs for pointers to functions with arguments. I recall that it took me a while to get around to such a definition while trying to understand a numerical algorithm written in C a while ago. So, could you share your tips and thoughts on how to write good typedefs for pointers to functions (Do's and Do not's), as to why are they useful and how to understand others' work? Thanks!

推荐答案

考虑信号()从C标准功能:

extern void (*signal(int, void(*)(int)))(int);

完美隐晦明显 - 这是一个函数,有两个参数,一个整数和一个指针,它接受一个整数作为参数并没有返回值的函数,它(信号())返回一个指针,它接受一个整数作为参数,并且没有返回的功能。

Perfectly obscurely obvious - it's a function that takes two arguments, an integer and a pointer to a function that takes an integer as an argument and returns nothing, and it (signal()) returns a pointer to a function that takes an integer as an argument and returns nothing.

如果你写的:

typedef void (*SignalHandler)(int signum);

然后就可以代替声明信号()为:

extern  SignalHandler signal(int signum, SignalHandler handler);

这意味着同样的事情,但通常被认为是比较容易阅读。它是清晰的,该函数接受一个 INT SignalHandler 并返回 SignalHandler

This means the same thing, but is usually regarded as somewhat easier to read. It is clearer that the function takes an int and a SignalHandler and returns a SignalHandler.

这需要一些时间来适应,虽然。有一件事你不能这样做,虽然是写在函数定义中使用 SignalHandler 的typedef 的信号处理函数

It takes a bit of getting used to, though. The one thing you can't do, though is write a signal handler function using the SignalHandler typedef in the function definition.

我是老派的prefers调用一个函数指针仍然为:

I'm still of the old-school that prefers to invoke a function pointer as:

(*functionpointer)(arg1, arg2, ...);

现代语法只使用:

Modern syntax uses just:

functionpointer(arg1, arg2, ...);

我可以看到为什么工作 - 我只是preFER知道,我需要寻找其中的变量初始化,而不是一个名为 functionpointer 功能。

山姆说:

我以前见过这样的解释。然后,像现在的话,我想我没有得到的是两个语句之间的连接:

I have seen this explanation before. And then, as is the case now, I think what I didn't get was the connection between the two statements:

    extern void (*signal(int, void()(int)))(int);  /*and*/

    typedef void (*SignalHandler)(int signum);
    extern SignalHandler signal(int signum, SignalHandler handler);


  
  

或者,我想问的是,什么是一个可以利用拿出你的第二个版本的基本概念?什么是连接SignalHandler和第一类型定义的基本?我觉得这里需要解释的是什么,是的typedef实际上是在这里做什么。

Or, what I want to ask is, what is the underlying concept that one can use to come up with the second version you have? What is the fundamental that connects "SignalHandler" and the first typedef? I think what needs to be explained here is what is typedef is actually doing here.

让我们再试一次。其中第一项是直接从C标准提升 - 我重新输入它,并检查了我有右括号(直到我纠正它 - 它是一个艰难的cookie来记住)。

Let's try again. The first of these is lifted straight from the C standard - I retyped it, and checked that I had the parentheses right (not until I corrected it - it is a tough cookie to remember).

首先,请记住,的typedef 引入一个别名有型。因此,别名是 SignalHandler ,它的类型是:

First of all, remember that typedef introduces an alias for a type. So, the alias is SignalHandler, and its type is:

一个指针,它接受一个整数作为参数,并且没有返回。函数

a pointer to a function that takes an integer as an argument and returns nothing.

在'返回任何'部分的拼写无效;这是一个整数的说法是(我相信)不言自明的。下面的符号是根本(或没有)的规定和C如何法术指针函数获取参数返回给定类型:

The 'returns nothing' part is spelled void; the argument that is an integer is (I trust) self-explanatory. The following notation is simply (or not) how C spells pointer to function taking arguments as specified and returning the given type:

type (*function)(argtypes);

创建信号处理程序类型后,我可以用它来声明变量等。例如:

After creating the signal handler type, I can use it to declare variables and so on. For example:

static void alarm_catcher(int signum)
{
    fprintf(stderr, "%s() called (%d)\n", __func__, signum);
}

static void signal_catcher(int signum)
{
    fprintf(stderr, "%s() called (%d) - exiting\n", __func__, signum);
    exit(1);
}

static struct Handlers
{
    int              signum;
    SignalHandler    handler;
} handler[] =
{
    { SIGALRM,   alarm_catcher  },
    { SIGINT,    signal_catcher },
    { SIGQUIT,   signal_catcher },
};

int main(void)
{
    size_t num_handlers = sizeof(handler) / sizeof(handler[0]);
    size_t i;

    for (i = 0; i < num_handlers; i++)
    {
        SignalHandler old_handler = signal(handler[i].signum, SIG_IGN);
        if (old_handler != SIG_IGN)
            old_handler = signal(handler[i].signum, handler[i].handler);
        assert(old_handler == SIG_IGN);
    }

    ...continue with ordinary processing...

    return(EXIT_SUCCESS);
}

请注意如何避免在信号处理程序使用的printf()

那么,我们还有什么这里所做的 - 除了这将需要使code编译干净省略4个标准的头文件

So, what have we done here - apart from omit 4 standard headers that would be needed to make the code compile cleanly?

前两个函数是采取单一的整数,返回任何功能。其中一人竟根本不感谢退出(1)返回; 但对方并打印信息后返回。请注意,C标准不允许你做的非常的信号处理器中; POSIX 是在什么是允许多一点大度,但官方不认可调用 fprintf中()。我还打印出所接收到的信号编号。在 alarm_handler()函数,该值将永远是 SIGALRM ,因为这是它是一个处理程序的唯一信号对,但 signal_handler()可能会 SIGINT SIGQUIT 如,因为相同的功能是用于两个信号编号

The first two functions are functions that take a single integer and return nothing. One of them actually doesn't return at all thanks to the exit(1); but the other does return after printing a message. Be aware that the C standard does not permit you to do very much inside a signal handler; POSIX is a bit more generous in what is allowed, but officially does not sanction calling fprintf(). I also print out the signal number that was received. In the alarm_handler() function, the value will always be SIGALRM as that is the only signal that it is a handler for, but signal_handler() might get SIGINT or SIGQUIT as the signal number because the same function is used for both.

然后,我创建结构的阵列,其中每个元素标识要安装该信号的信号数和处理程序。我选择了担心3信号;我经常担心 SIGHUP SIGPIPE SIGTERM 也和他们是否被定义( #IFDEF 条件编译),但只是复杂的事情。我也可能会使用POSIX 的sigaction()而不是信号(),但那是另一个问题;让我们坚持我们之前的。

Then I create an array of structures, where each element identifies a signal number and the handler to be installed for that signal. I've chosen to worry about 3 signals; I'd often worry about SIGHUP, SIGPIPE and SIGTERM too and about whether they are defined (#ifdef conditional compilation), but that just complicates things. I'd also probably use POSIX sigaction() instead of signal(), but that is another issue; let's stick with what we started with.

的main()函数在处理程序列表迭代进行安装。对于每一个处理程序,它首先调用信号()来找出进程是否正在无视信号,而这样做,安装 SIG_IGN 作为处理程序,它保证信号保持被忽略。如果信号没有previously被忽略,然后调用信号(),这一次安装pferred信号处理程序中的$ P $。 (其它值是presumably SIG_DFL ,该信号默认的信号处理)。因为第一个电话信号()设定的处理程序 SIG_IGN 信号()返回previous错误处理程序,如果语句后必须 SIG_IGN - 因此断言。 (当然,也可能是 SIG_ERR ,一旦发生显着错误的 - 但后来我了解到,从断言射击。)

The main() function iterates over the list of handlers to be installed. For each handler, it first calls signal() to find out whether the process is currently ignoring the signal, and while doing so, installs SIG_IGN as the handler, which ensures that the signal stays ignored. If the signal was not previously being ignored, it then calls signal() again, this time to install the preferred signal handler. (The other value is presumablySIG_DFL, the default signal handler for the signal.) Because the first call to 'signal()' set the handler to SIG_IGN and signal() returns the previous error handler, the value of old after the if statement must be SIG_IGN - hence the assertion. (Well, it could be SIG_ERR if something went dramatically wrong - but then I'd learn about that from the assert firing.)

然后程序做它的东西,正常退出。

The program then does its stuff and exits normally.

请注意,一个函数的名称可被视为一个指针为适当的类型的功能。当你不使用的函数调用括号 - 作为初始化,例如 - 函数名称将成为一个函数指针。这也是为什么它是合理的通过 pointertofunction(ARG1,ARG2)符号来调用功能;当你看到 alarm_handler(1),你可以认为 alarm_handler 是一个函数指针,因此 alarm_handler(1)是通过函数指针的函数的调用。

Note that the name of a function can be regarded as a pointer to a function of the appropriate type. When you do not apply the function-call parentheses - as in the initializers, for example - the function name becomes a function pointer. This is also why it is reasonable to invoke functions via the pointertofunction(arg1, arg2) notation; when you see alarm_handler(1), you can consider that alarm_handler is a pointer to the function and therefore alarm_handler(1) is an invocation of a function via a function pointer.

所以,到目前为止,我展示了一个 SignalHandler 变量是相对直接的使用,只要你有一定的权利类型的值分配给它 - 这是两个信号处理功能提供了

So, thus far, I've shown that a SignalHandler variable is relatively straight-forward to use, as long as you have some of the right type of value to assign to it - which is what the two signal handler functions provide.

现在我们回到这个问题 - 如何为做这两个声明的信号()之间的相互关系。

Now we get back to the question - how do the two declarations for signal() relate to each other.

让我们回顾一下第二个声明:

Let's review the second declaration:

 extern SignalHandler signal(int signum, SignalHandler handler);

如果我们改变了函数名和这样的类型:

If we changed the function name and the type like this:

 extern double function(int num1, double num2);

您也没问题间preting这是一个 INT 双击一个函数,作为参数,并返回一个双击值(你会也许你最好不要'干完了,如果这是有问题的 - 但也许你应该谨慎发问硬如这一个,如果这是一个问题)。

you would have no problem interpreting this as a function that takes an int and a double as arguments and returns a double value (would you? maybe you'd better not 'fess up if that is problematic - but maybe you should be cautious about asking questions as hard as this one if it is a problem).

现在,而不是作为一个双击信号()函数接受一个 SignalHandler 作为第二个参数,并返回一个作为其结果。

Now, instead of being a double, the signal() function takes a SignalHandler as its second argument, and it returns one as its result.

,通过这种也可被视为该力学

The mechanics by which that can also be treated as:

extern void (*signal(int signum, void(*handler)(int signum)))(int signum);

是棘手的解释 - 所以我可能会搞砸了。这一次,我给的参数的名称 - 尽管这些名字并不重要。

are tricky to explain - so I'll probably screw it up. This time I've given the parameters names - though the names aren't critical.

在一般情况下,在C中,声明机构是这样的,如果你写:

In general, in C, the declaration mechanism is such that if you write:

type var;

然后当你写 VAR 将其重新presents给定的键入的值。例如:

then when you write var it represents a value of the given type. For example:

int     i;            // i is an int
int    *ip;           // *ip is an int, so ip is a pointer to an integer
int     abs(int val); // abs(-1) is an int, so abs is a (pointer to a)
                      // function returning an int and taking an int argument

在标准,的typedef 被视为在语法存储类,而不是像静态的extern 是存储类。

In the standard, typedef is treated as a storage class in the grammar, rather like static and extern are storage classes.

typedef void (*SignalHandler)(int signum);

意味着,当你看到类型的变量 SignalHandler (比如alarm_handler)援引为:

means that when you see a variable of type SignalHandler (say alarm_handler) invoked as:

(*alarm_handler)(-1);

结果具有类型为void - 就是没有结果。和(* alarm_handler)( - 1); alarm_handler()与参数 1

the result has type void - there is no result. And (*alarm_handler)(-1); is an invocation of alarm_handler() with argument -1.

所以,如果我们宣布:

extern SignalHandler alt_signal(void);

这意味着:

(*alt_signal)();

再presents一个空值。因此:

represents a void value. And therefore:

extern void (*alt_signal(void))(int signum);

是等效的。现在,信号()更复杂,因为它不仅返回 SignalHandler ,它也接受一个int和 SignalHandler 作为参数:

is equivalent. Now, signal() is more complex because it not only returns a SignalHandler, it also accepts both an int and a SignalHandler as arguments:

extern void (*signal(int signum, SignalHandler handler))(int signum);

extern void (*signal(int signum, void (*handler)(int signum)))(int signum);

如果仍然迷惑你,我不知道如何帮助 - 它仍然是一些神秘的水平给我,但我已经习惯了它是如何工作的,因此可以告诉你,如果你坚持下去的25年左右的时间,它会成为你的第二天性(甚至有点快,如果你是聪明的)。

If that still confuses you, I'm not sure how to help - it is still at some levels mysterious to me, but I've grown used to how it works and can therefore tell you that if you stick with it for another 25 years or so, it will become second nature to you (and maybe even a bit quicker if you are clever).

这篇关于了解在C函数指针的typedef的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆