C - 高阶函数

时间:2017-01-20 20:07:47

标签: c

Signal将回调函数作为其参数之一。要拥有变量行为,我想在函数中创建一个函数。这是我到目前为止的尝试:

typedef void (*sighandler_t)(int);
sighandler_t f(int pid) {
    void sigintHandler(int sig) {
        printf("Process %d", pid);
    }
    return sigintHandler
}

int main(void) {
    ...
    if (signal(SIGTSTP, *f(1)) == SIG_ERR) {
        ...
    }
    ...
}

但是,每次发送SIGTSTP(Ctrl-z)时,都会出现seg错误。

作为旁注:我们非常感谢任何有关如何调试seg故障的提示!

2 个答案:

答案 0 :(得分:2)

您的代码编译是因为它在语法上是正确的并且您正在使用编译器扩展;但是,您的代码可能存在一些可能导致segfault

的基本问题

首先,您的信号处理程序代码:

typedef void (*sighandler_t)(int);
sighandler_t f(int pid) {
    void sigintHandler(int sig) {
        printf("Process %d", pid);
    }
    return sigintHandler;
}

这是not standard C,甚至要求在gcc的某些版本上指定-ftrampolines标志才能实际编译。

您的信号处理函数本身有一些问题需要解决:

sigintHandlernested function,因此当您的信号处理函数f返回return sigintHandler;时,您将返回一个函数指针。

在你的代码中,这是正确编译的,因为你有typedef void (*sighandler_t)(int);,它定义了一个函数指针类型,它可以指向具有void返回类型的函数,并将int作为一个参数,sigintHandler定义为。

相反,您的信号处理函数可以简单地写为:

void sigintHandler(int sig) {
    printf("Signal %d\n", sig);
}

在您的主要功能中,您有以下内容:

if (signal(SIGTSTP, *f(1)) == SIG_ERR) {
    // ....    
}

这里应该注意到这一点也存在一些问题。首先,signal函数将信号编号(通常是signal.h标头中定义的宏)作为其第一个参数,并将第二个参数指向定义为{{1的函数的指针}}

对此,您调用函数而不是将其作为指针传递。

void func_name(int sig)实际调用*f(1)传递f作为参数;相反,您可以将其更改为以下内容:

1

但是这应该发出警告/错误,因为if (signal(SIGTSTP, f) == SIG_ERR) { // .... } 被定义为返回函数指针而不是f

因此,要将代码更改为符合要求,您可以执行以下操作:

void

你说过:

  

拥有可变行为......

这取决于您想要的变量性质,但如果它基于信号的变量函数,您可以执行以下操作:

#include <stdio.h>
#include <signal.h>

void sigintHandler(int sig) {
    printf("Signal %d", sig);
}

int main(void) {
    // ...
    if (signal(SIGTSTP, sigintHandler) == SIG_ERR) {
        // ...
    }
    // ...
    return 0;
}

或者您可以使用#include <stdio.h> #include <signal.h> #include <unistd.h> void sig_stop(int sig) { printf("Process %d stop\n", getpid()); } void sig_int(int sig) { printf("Process %d interrupt\n", getpid()); } int main(void) { // ... if (signal(SIGTSTP, sig_stop) == SIG_ERR) { // ... } if (signal(SIGINT, sig_int) == SIG_ERR) { // ... } // ... return 0; } 声明:

switch
  

关于如何调试seg故障的任何提示都非常感谢!

首先,了解segmentation fault是什么;然后,您可以使用gdb之类的调试器来逐步执行代码或检查故障转储,以查看#include <stdio.h> #include <signal.h> #include <unistd.h> void sigHandler(int sig) { printf("Process %d received %d\n", getpid(), sig); switch (sig) { case SIGTSTP: // do stop code break; case SIGINT: // do interupt code break; } } int main(void) { // ... if (signal(SIGTSTP, sigHandler) == SIG_ERR) { // ... } if (signal(SIGINT, sigHandler) == SIG_ERR) { // ... } // ... return 0; } 具体发生的位置。

希望可以提供帮助。

答案 1 :(得分:-1)

确定您的要求,但我可以帮助您了解您的细分错误。

当你调用一个函数时,有几件事情已经完成。

  1. 将函数参数推送到堆栈
  2. 将返回地址推送到堆栈
  3. 跳转到功能地址
  4. 功能正文
  5. 来自堆栈的pop返回地址
  6. 来自堆栈的pop函数参数。
  7. 当呼叫范围完成1,2,6时。

    如您所知,signal是void arguments因此调用(1)会将0个参数推送到堆栈。

    而return(6)会从堆栈中弹出“不存在的”int并破坏它。

    有点解决方案

    你不能使用参数信号功能,
    你能做的是:

    1. 您可以在信号函数中读取全局变量。因此,请阅读该计划的当前状态。

    2. 你可以通过sys_call获取你的process_id,thread_id。

    3. 我不推荐,但你可以进一步读取你的堆栈到前一个范围并获取它的局部变量。使用BIG注意,它不是你设置信号的功能......而是信号瞬间运行的功能。