C 主回路没有';我不能正常退出

C 主回路没有';我不能正常退出,c,terminal,atexit,C,Terminal,Atexit,我创建了此功能,作为基于终端的应用程序的主菜单: bool wizard_run() { char *command = NULL; bool repeat = false; bookmark: terminal_prepare(); terminal_message(MESSAGE_INTRODUCTION); loop: /* repeat until a valid command */ { free(command); /* free

我创建了此功能,作为基于终端的应用程序的主菜单:

bool wizard_run() {
   char *command = NULL;
   bool repeat = false;
   bookmark:

   terminal_prepare();
   terminal_message(MESSAGE_INTRODUCTION);

   loop: /* repeat until a valid command */ {
      free(command); /* free any previous command */
      command = terminal_command(PROMPT_COMMAND, COMMAND_LENGTH, repeat);

      if (!strcmp(command, COMMAND_ENCRYPT)) wizard_encrypt();
      else if (!strcmp(command, COMMAND_DECRYPT)) wizard_decrypt();
      else if (!strcmp(command, COMMAND_CONCEAL)) wizard_conceal();
      else if (!strcmp(command, COMMAND_REVEAL)) wizard_reveal();
      else if (!strcmp(command, COMMAND_ERASE)) wizard_erase();
      else if (!strcmp(command, COMMAND_GENERATE)) wizard_generate();
      else if (!strcmp(command, COMMAND_NAVIGATE)) directory_navigate(RDS_HOME)
      else if (!strcmp(command, COMMAND_SESSION)) wizard_session();
      else if (!strcmp(command, COMMAND_SAFEMODE)) wizard_safemode();
      else if (!strcmp(command, COMMAND_HELP)) wizard_help(HELP_MAINMENU)
      else if (!strcmp(command, COMMAND_EXIT)) {free(command); return false;}
      else {repeat = true; goto loop;}
   }

   free(command); /* free last command */
   return true;
}
void main() {
   // initialize any Components needed
   if (!directory_rdsload()) return;
   packager_reset();

   // show the Splash Screen
   SPLASH();

   // continuously execute the Main Thread
   while (wizard_run()); /* execution loop */

   // perform any Clean-UP needed before exit
   reset();
   encryption_reset();
   directory_reset(is_safemode);
}
所有大写值都是
#定义的
常量,所有
…(…)
都是函数,除了
向导帮助(…)
目录导航(…)
都是宏(这就是为什么这些行后面没有分号的原因)

这是应用程序的主要功能:

bool wizard_run() {
   char *command = NULL;
   bool repeat = false;
   bookmark:

   terminal_prepare();
   terminal_message(MESSAGE_INTRODUCTION);

   loop: /* repeat until a valid command */ {
      free(command); /* free any previous command */
      command = terminal_command(PROMPT_COMMAND, COMMAND_LENGTH, repeat);

      if (!strcmp(command, COMMAND_ENCRYPT)) wizard_encrypt();
      else if (!strcmp(command, COMMAND_DECRYPT)) wizard_decrypt();
      else if (!strcmp(command, COMMAND_CONCEAL)) wizard_conceal();
      else if (!strcmp(command, COMMAND_REVEAL)) wizard_reveal();
      else if (!strcmp(command, COMMAND_ERASE)) wizard_erase();
      else if (!strcmp(command, COMMAND_GENERATE)) wizard_generate();
      else if (!strcmp(command, COMMAND_NAVIGATE)) directory_navigate(RDS_HOME)
      else if (!strcmp(command, COMMAND_SESSION)) wizard_session();
      else if (!strcmp(command, COMMAND_SAFEMODE)) wizard_safemode();
      else if (!strcmp(command, COMMAND_HELP)) wizard_help(HELP_MAINMENU)
      else if (!strcmp(command, COMMAND_EXIT)) {free(command); return false;}
      else {repeat = true; goto loop;}
   }

   free(command); /* free last command */
   return true;
}
void main() {
   // initialize any Components needed
   if (!directory_rdsload()) return;
   packager_reset();

   // show the Splash Screen
   SPLASH();

   // continuously execute the Main Thread
   while (wizard_run()); /* execution loop */

   // perform any Clean-UP needed before exit
   reset();
   encryption_reset();
   directory_reset(is_safemode);
}
这里,只有
SPLASH()
是宏

所有布尔值(即
true
false
)都是预定义的无符号字符值,
bool
typedef无符号字符bool

它的正常行为是在用户输入
exit
(命令的当前值)时退出
向导运行()
循环,执行
reset();加密重置();目录重置(是安全模式)然后正常终止应用程序。相反,当我第一次键入
exit
时,它会重新显示菜单,当我第二次键入
exit
时,它会终止,并出现以下错误:

application: cxa_atexit.c:99: __new_exitfn: Assertion `l != ((void *)0)' failed.
Aborted (core dumped)
当程序在
gdb
下运行时,我得到以下错误:

application: cxa_atexit.c:99: __new_exitfn: Assertion `l != ((void *)0)' failed.

Program received signal SIGABRT, Aborted.
0xb7fdd424 in __kernel_vsyscall ()
除了
main()
之外,没有其他函数调用
wizard\u run()
,因此这不是菜单重新显示而不是退出的原因。有什么想法吗


提前谢谢!!!:好的,我终于找到问题了!它位于
SPLASH()
中。这是一个宏,定义为:

#define SPLASH() { \
   if (!vfork()) execlp("/bin/splash", "/bin/splash", NULL); \
   else wait(); \
}
由于我的应用程序仍处于测试阶段,我没有将
splash
放入目录
/bin
。我从没想过这会是个问题。然而,这里有一个陷阱:我使用
vfork
而不是
fork
,因为我只想执行一个新程序(
vfork
在不需要子进程作为父进程副本时更快)。由于不存在
/bin/bash
,execlp没有为子进程加载新的应用程序映像。因此,我可以想象这两个进程共享了它们的一部分图像?()

因此,当调用
SPLASH()
时,子进程将接管并再次显示主菜单。一切看起来都很好。但当我调用退出时,子菜单会正常终止,父菜单控制终端并重新显示主菜单(这就是为什么它会显示两次!)。当再次调用
exit
时,轮到父进程终止。但是,与父进程共享同一进程映像的子进程已经终止,从而改变了一些数据。一定是这些修改过的数据导致了退出时的错误

当我的功能清除终端并写入以前的消息时,我没有注意到子进程首先显示主菜单!;)


我对整个问题的性质感到非常兴奋。虽然我无法更好地解释它(我一定是在我的术语中犯了一些错误),但我希望你理解哪里出了问题!感谢大家的宝贵意见(尤其是WhozCraig)!!:D

在调试器下运行此操作,当该断言触发时,我几乎可以保证您将得到一个堆栈跟踪,确切地说明发生这种情况的原因。您有太多我们看不到代码的函数调用(
terminal\u command()
将是一个非常可疑的函数调用)。另请注意:
wizard\u run()
中的
goto
是完全不需要的;清除它并使用适当的
while
-loop.Function
terminal_command()
使用
printf
scanf
打印提示消息并读取用户输入(命令)。最后一个布尔值确定是覆盖终端中的上一个提示还是转到新行。我要做的是启动应用程序并立即键入
exit
。显示的错误转储看起来是因为exit handler函数链在不应该被覆盖时被覆盖。如果在程序中没有任何其他障碍的情况下发生这种情况,请首先在主程序中注释while循环到
wizard\u run()
。如果这个“有效”,那么把它带回来并注释掉循环中的所有垃圾,直到只剩下退出逻辑。。一次一块地添加,直到它重新生成。我相信您或您使用的代码正在以未定义的行为方式写入内存,并且与所有未定义的行为一样,很难找到它。如果您的命令阅读器非常简单,请发布它。如果它不是问题的根源,那就顺其自然吧,但这里有大量函数,其中任何一个都可能导致问题,我们都看不到问题的代码。如果没有明确的代码,你将得到最好的猜测,但没有具体的。