C++:当我的应用程序在随机位置崩溃时从哪里开始?

Mar*_*aux 6 c++ crash debugging

我正在开发一款游戏,当我在游戏中执行特定操作时,它会崩溃.所以我进行了调试,我看到我的应用程序在简单的C++语句中崩溃,例如if,return...每次重新运行时,它会在3行中的一行中随机崩溃,但它永远不会成功.

第1行:

if (dynamic) { ... } // dynamic is a bool member of my class
Run Code Online (Sandbox Code Playgroud)

第2行:

return m_Fixture; // a line of the Box2D physical engine. m_Fixture is a pointer.
Run Code Online (Sandbox Code Playgroud)

第3行:

return m_Density; // The body of a simple getter for an integer.
Run Code Online (Sandbox Code Playgroud)

我从应用程序和操作系统都没有错误...

是否有提示,提示或技巧来调试更有效率并了解正在发生的事情?

这就是我喜欢Java的原因......

谢谢

Ada*_*mke 10

像这样的随机崩溃通常是由堆栈损坏引起的,因为这些是分支指令,因此对堆栈的状况很敏感.这些有点难以追踪,但您应该运行valgrind并检查每次崩溃时的调用堆栈,以尝试识别可能是错误根本原因的常见函数.


Eri*_*ric 5

这些主要是由于堆栈损坏,但堆损坏也会以这种方式影响程序.

由于"一个错误的关闭",大多数时候都会发生堆栈损坏.
发生堆损坏是因为没有小心处理new/delete,比如double delete.

基本上会发生的事情是溢出/损坏会覆盖一条重要的指令,然后在你试图执行指令时,它会崩溃.


Sig*_*erm 5

是否有提示、技巧或技巧可以更有效地调试并了解正在发生的事情?

  1. 在调试器中运行游戏,在崩溃点,检查所有参数的值。使用 Visual Studio 监视窗口或使用 gdb。使用“调用堆栈”检查父例程,尝试思考可能出错的地方。
  2. 在可疑(可能与崩溃相关)例程中,考虑将所有参数转储到 stderr(如果您使用的是 libsdl 或在 *nixlike 系统上),或写入日志文件,或使用(在 Windows 上)OutputDebugString 发送所有错误消息的副本。这将使它们在 Visual Studio 或调试器的“输出”窗口中可见。你也可以写“跟踪”(日志(“函数 %s 被调用”,__FUNCTION__))
  3. 如果您不能立即调试,则在崩溃时生成核心转储。在 Windows 上它可以使用 MiniDumpWriteDump 来完成,在 linux 上它被设置在配置变量的某个地方。核心转储可以由调试器处理。我不确定 VS express 是否可以在 Windows 上处理它们,但您仍然可以使用 WinDBG 调试它们。
  4. 如果在类内发生崩溃,请检查 *this 参数。它可能无效或为零。
  5. 如果错误确实是邪恶的(多线程应用程序中难以捉摸的堆栈损坏导致延迟崩溃),请编写自定义内存管理器,该管理器将覆盖 new/delete,提供 malloc 的替代方案(如果您的应用程序出于某种原因使用它,这可能是可能的) ),并且使用 VirtualProtect (windows) 或特定于操作系统的替代方案锁定所有未使用的内存。在这种情况下,所有具有潜在危险的操作都会立即使应用程序崩溃,这将允许您调试问题(如果您有即时调试器)并立即找到危险的例程。我更喜欢这种“自定义内存管理器”而不是 boundschecker 等等——因为根据我的经验,它更有用。作为替代方案,您可以尝试使用仅在 linux 上可用的 valgrind。请注意,如果您的应用程序非常频繁地分配内存,您
  6. 在需要健全性检查的区域,要么使用 ASSERT,要么(IMO 更好的解决方案)编写一个例程,如果不满足某些条件,该例程将使应用程序崩溃(通过抛出带有有意义消息的 std::exception)。
  7. 如果您发现了有问题的例程,请使用调试器的 step into/step over 来遍历它。观看争论。
  8. 如果您确定了一个有问题的例程,但由于某种原因无法直接调试它,则在该例程中的每个语句之后,将所有变量转储到 stderr 或日志文件(fprintf 或 iostreams - 您的选择)。然后分析输出并思考它是如何发生的。确保在每次写入后刷新日志文件,否则您可能会错过崩溃前的数据。

一般来说,您应该很高兴应用程序在某处崩溃。崩溃意味着您可以使用调试器快速找到并消除错误。不会使程序崩溃的错误要困难得多(真正复杂的错误示例:给定 100000 个输入值,在对值进行数百次操作后,在数千个输出中,应用程序产生 1 个绝对不正确的结果,这不应该发生了)

这就是我喜欢Java的原因...

对不起,如果你不能处理语言,那完全是你的错。如果您无法使用该工具,请选择另一个工具或提高您的技能。顺便说一句,可以用Java制作游戏。