我正在编写一个游戏,其中许多不同的条件都可能导致失败,例如图像或着色器无法加载,OpenGL 无法获得有效的上下文等。

在理想的世界中,我想以我认为适用于 C 的方式使用退出代码,即对于失败条件非零。但是,有一些因素会影响这一点:

  • 在使用非零退出代码时,没有调用 atexit() ,对我来说很不幸,因为我的程序清理是通过 atexit() 中的单个 main() 注册分层处理的,我喜欢。
  • 我突然想到,鉴于应用程序的性质 - 与批处理程序不同,游戏具有无限的运行时间 - 可能永远不会出现实际使用退出代码的实际情况。它不像例如一个由 Makefile 调用的命令行图像转换器,它需要通过其退出代码知道程序是成功还是失败,从而确定是否继续构建。可能是,如果程序在运行时出现任何问题,例如丢失文件,那是我自己修复的问题,然后顺利进行。

  • 在我的情况下,有哪些支持和反对使用非零退出代码的论据?

    最佳答案

    触发注册的 atexit() 函数

    与您的问题相反,即使程序尝试向主机环境返回非零终止状态,仍会调用使用 atexit() 注册的函数。调用 exit() 或返回的 main() 函数将触发使用 atexit() 注册的函数,无论给定的值如何。

    例子:

    #include <stdlib.h>
    #include <stdio.h>
    
    void print_stuff()
    {
        puts("Stuff");
    }
    
    int main()
    {
        atexit(print_stuff);
        exit(1);
    }
    

    这将打印 Stuff ,即使返回 1

    技术细节

    按照 ISO C 标准,atexit() 中注册的函数是在调用 exit() 之后调用的。以下情况也被定义为调用 exit() (并因此触发使用 atexit() 注册的函数):
  • main() 返回相当于调用 exit()
  • 最后一个线程调用 thrd_exit() 后,调用 exit(EXIT_SUCCESS)

  • 以下是调用 exit() 的可能实现定义的来源:
  • SIGTERM 的默认信号处理程序。
  • 使用 set_constraint_handler_s() 之前的默认约束处理程序。

  • 该标准提到了以下情况,其中 exit() 和对使用 atexit() 注册的函数的调用被规避:
  • 未处理的 SIGABRTSIGABRT 完成由注册 signal() 的函数处理。 SIGABRT 可以由 abort() 引发。
  • 调用 _Exit()
  • 调用 quick_exit()

  • 在某些情况下,您的实现的宿主环境可能会终止程序,而不会调用使用 atexit() 注册的函数,例如在 segfault 之后。

    关于游戏的退出代码

    您选择的退出代码对于游戏来说应该没有太大关系。是的,您不会依赖 shell 脚本来运行您的游戏并向用户报告错误。对于像 Linux 这样的系统,错误反馈可能以对话框弹出窗口、日志或 stderr 的形式更有用。

    关于实时/非批处理应用程序中的 C 退出代码和 atexit(),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/31864476/

    10-10 17:39