如何将参数传递给atexit()内部的函数,C

blmhpbnm  于 2023-04-05  发布在  其他
关注(0)|答案(4)|浏览(170)

我有一个函数,它可以从网络上做一些免费和unregs:

void UNREG_on_exit(COT_arguments args, Node_Information *node)

我试图让它在我退出程序的时候被调用(即使使用Ctrl+C)
问题是我如何传递参数?从来没有做过函数指针,也不能真正找到答案。
目前我有:

void UNREG_on_exit(args, &node); // This is how you make the pointer?
atexit(UNREG_on_exit);

// or //

atexit((void) {
    UNREG_on_exit(args, &node); // Or like this?
});

我不知道,他们两个都给予了我一个错误。
“需要标识符”,参数下的一条红线
其他信息:

typedef struct Node_Information
{
    int id;
    int net;
    Backup bck;
    Extern ext;
    Intern *intern_list;
    int InNetword;
    int debug_mode;
    int IsREGED;
    int fd;
    Table *table_list;
    StringList *contents_list;
    PendingConnections *pending_connections_list;
    QueryList *query_list;

} Node_Information;

typedef struct COT_arguments
{
    char *IP;
    int TCP;
    char *reg_IP;
    int reg_UDP;
} COT_arguments;
z0qdvdin

z0qdvdin1#

atexit注册的函数不能带参数。它的签名必须是void (*)(void),即一个不返回任何参数的函数。
你需要使用全局变量,这些变量在程序中的其他地方被设置,函数可以使用。

i34xakig

i34xakig2#

atexit不支持带参数的函数,所以你既不能注册这样一个函数,也不能给它传递任何参数。你必须传递一个void(*)(void)类型的函数指针-不带参数,不返回值。
因此,必须通过一些其他方法来处理该部分,例如,使用文件范围变量。与任何像这样使用“回调函数”一样,要注意不要使用任何指向本地数据的指针,而只使用指向静态存储或动态分配内存的指针。
还要注意,atexit调用函数的顺序与FIFO相反,也就是说,最后注册的函数将首先执行。
示例:

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

static char* str1;
static char* str2;

void this_is_the_end1 (void) { puts(str1); }
void this_is_the_end2 (void) { puts(str2); }

int main (void)
{
  str1 = "The end of everything that stands";
  atexit(this_is_the_end1);
  str2 = "The end of our elaborate plans";
  atexit(this_is_the_end2);

  puts("This is the end, beautiful friend");
  puts("This is the end, my only friend");
}

输出顺序由ISO C:

This is the end, beautiful friend
This is the end, my only friend
The end of our elaborate plans
The end of everything that stands
hmmo2u0o

hmmo2u0o3#

我有一个函数,它可以从网络上做一些免费和unregs:

void UNREG_on_exit(COT_arguments args, Node_Information *node)

我试图让它在我退出程序的时候被调用(即使使用Ctrl+C)

帧挑战:为什么?

特别是为什么要担心在程序退出时释放分配的内存,因为无论您是否显式释放它,系统都会释放它。它还将关闭进程的打开文件,包括网络连接。这可能不会产生与远程网络服务的干净的应用程序级断开连接,但任何此类服务都需要对此类事件具有鲁棒性。
我写过很多软件,但我不记得曾经注册过退出处理程序。我安排在程序进行时在适当的点清理资源,并在正常终止时显式地执行任何最终清理。这比使用退出处理程序更清楚,也不那么神秘。我很少看到需要制定特殊的规定来处理异常终止。而且不可能处理 * 所有 * 异常终止。即使没有无法捕获或处理的致命信号,也没有针对突然断电或灾难性硬件故障的软件级保护。
但是如果你坚持尝试处理异常终止,那么你将需要更加努力地工作。标准atexit()和特定于GNU的on_exit()注册的退出处理程序在 * 正常 * 程序终止时运行,这意味着当exit()函数被调用或对main()的初始调用返回时。当进程被信号杀死时,它们不会运行(这是Ctrl+C产生的一种术语),或者当它由于调用_exit()_Exit()而终止时,或者当它停止运行而不终止时,例如通过调用execve()
你可能可以避免调用_Exit()和其他有问题的函数,但是要在接收到信号时进行清理,你需要为 * 所有 * 默认处理是终止程序的信号注册一个信号处理程序。或者至少为那些可以被捕获的信号注册一个信号处理程序,这不是所有的信号。此外,信号处理程序可能只调用异步信号安全函数,我不确定你的“unregs”需要什么,所以也许这些都可以,但是free()不是async-signal-safe的。exit()也不是,以免你认为你可以调用它来将异常终止转换为正常终止。
至于**使用atexit()**处理正常终止情况的细节,atexit()的签名是

int atexit(void (*function)(void));

因此,函数指针参数应该指向一个没有参数也不返回任何东西的函数,这就是它的调用方式。这样一个函数所处理的任何数据都必须是全局范围的(全局变量,环境变量,进程自己的PID,...)或可被该函数发现的(一天中的时间,某个已知目录中的文件,...)。示例:

static void *allocated;

static void cleanup_handler(void) {
    free(allocated);
}

int main(void) {
    at_exit(cleanup_handler);
    // ...
}

您的特定C实现可能会提供其他替代方案,例如 * GNU特定的 * on_exit()。这些细节会有所不同。on_exit()本身允许您注册数据指针沿着回调指针,以便在调用函数时传递给函数。这就是您向该函数提供数据的方式,但请注意,数据必须具有静态存储期限。否则当调用exit处理程序时,它的生存期已经结束。例如:

struct data_to_clean_up {
    void *allocated;
    // ...
} loose_ends;

void cleanup_handler(int status, void *data) {
    struct data_to_clean_up *loose_ends = (struct data_to_clean_up *) data;
    free(loose_ends->allocated);
    // ...
}

int main(void) {
    on_exit(cleanup_handler, &loose_ends);

    // ...
}

相关问题