我正在开发一款游戏,当我在游戏中执行特定操作时,它会崩溃。
所以我进行了调试,我看到我的应用程序崩溃了简单的C ++语句,如if
,return
,...每次重新运行时,它会在3行中的一行中随机崩溃,但从未成功
第1行:
if (dynamic) { ... } // dynamic is a bool member of my class
第2行:
return m_Fixture; // a line of the Box2D physical engine. m_Fixture is a pointer.
第3行:
return m_Density; // The body of a simple getter for an integer.
我从应用程序和操作系统中都没有错误...
是否有提示,提示或技巧可以更有效地调试并了解正在发生的事情?
这就是我喜欢Java的原因......
由于
答案 0 :(得分:10)
这样的随机崩溃通常是由堆栈损坏引起的,因为这些是分支指令,因此对堆栈的状况很敏感。这些有点难以追踪,但你应该运行valgrind并检查每次崩溃时的调用堆栈,以尝试识别可能是错误根本原因的常见函数。
答案 1 :(得分:5)
这些主要是由于堆栈损坏,但堆损坏也会以这种方式影响程序。
由于“一个错误的关闭”,大多数情况下都会发生堆栈损坏 发生堆损坏是因为没有仔细处理新的/删除,比如双删除。基本上发生的事情是溢出/损坏会覆盖一条重要的指令,然后很久以后,当你尝试执行指令时,它会崩溃。
答案 2 :(得分:4)
我通常喜欢花点时间退一步思考代码,试图捕捉任何逻辑错误。
您可以尝试注释掉代码的不同部分,看看它是否会影响程序的编译方式。
除了这两件事,你可以尝试使用像Visual Studio或Eclipse等调试器......
最后,您可以尝试发布您的代码以及您在具有知道编程的社区的网站上发生的错误,并且可以帮助您解决错误(读取:stackoverflow)
答案 3 :(得分:4)
是否有提示,提示或技巧可以更有效地调试并了解正在发生的事情?
一般来说,你应该对应用程序在某处崩溃感到高兴。崩溃意味着您可以使用调试器快速找到并消灭的错误。不会使程序崩溃的错误要困难得多(真正复杂的错误的例子:给定100000个输入值,经过数百次操作值,在数千个输出中,app产生1个绝对错误的结果,不应该有发生了一切)
这就是我喜欢Java的原因......
打扰一下,如果你不能处理语言,那完全是你的错。如果您无法处理该工具,请选择另一个工具或提高您的技能。顺便说一句,可以用java制作游戏。
答案 4 :(得分:3)
当您访问不允许访问的内存位置或尝试以不允许的方式访问内存位置时(例如,尝试写入只读方式),通常会发生崩溃/ Seg故障位置)。
有许多内存分析器工具,例如我使用Valgrind,这非常适合告诉问题是什么(不仅是行号,还有导致崩溃的原因)。
答案 5 :(得分:2)
没有简单的C ++语句。 if只是与您评估的条件一样简单。返回只是与您返回的表达式一样简单。
您应该使用调试器和/或发布一些崩溃的代码。 “我的应用程序崩溃”作为信息无法使用。
答案 6 :(得分:1)
之前我遇到过这样的问题。我试图从不同的线程刷新GUI。
答案 7 :(得分:1)
如果if
语句涉及解引用指针,那么你几乎肯定会破坏堆栈(这解释了为什么无辜的return 0
会崩溃......)
例如,这可能发生在数组中超出范围(您应该使用std::vector
!),尝试strcpy
基于char []的字符串缺少结尾{{ 1}}(您应该使用'\0'
!),将错误的大小传递给std::string
(您应该使用复制构造函数!)等。
尝试找出一种可靠地再现它的方法,然后将手表放在损坏的指针上。运行代码逐行,直到找到破坏指针的行。
答案 8 :(得分:1)
看一下反汇编。几乎所有的C / C ++调试器都会很高兴向您展示机器代码和程序崩溃的寄存器。寄存器包括指令指针(x86 / x64上的EIP或RIP),它是程序停止时的位置。其他寄存器通常具有存储器地址或数据。如果内存地址为0或指针错误,则存在问题。
然后你只需要向后工作就可以了解它是如何形成的。关于内存更改的硬件断点在这里非常非常有用。
在Linux / BSD / Mac上,使用GDB的脚本功能可以在这里提供很多帮助。您可以编写脚本,以便在断点被击中20次后,可以在数组元素17的地址上进行硬件监视。等等。
您也可以在程序中编写调试程序。使用assert()函数。到处!
使用assert检查每个函数的参数。在退出函数之前,使用assert检查每个对象的状态。在游戏中,断言玩家在地图上,玩家的健康状况在0到100之间,断言你能想到的一切。对于复杂的对象,将verify()或validate()函数写入对象本身,检查有关它的所有内容,然后从assert()中调用它们。
编写调试的另一种方法是让程序在Linux中使用signal()或在Windows中使用asm int 3从程序中进入调试器。然后,您可以将临时代码写入程序,以检查它是否在主循环的迭代1117321上。如果错误总是发生在1117322,这可能很有用。程序执行速度比使用调试器断点要快得多。
答案 9 :(得分:1)
一些提示:
- 在调试器下运行应用程序,将符号文件(PDB)放在一起
- How to set Visual Studio as the default post-mortem debugger?
- 为WinDbg设置默认调试器Just-in-time Debugging
- 检查内存分配Overriding new and delete和Overriding malloc and free
答案 10 :(得分:1)
另一个技巧:关闭代码优化并查看崩溃点是否更有意义。允许优化将代码中的一小部分浮动到令人惊讶的地方;回溯到源代码行的映射可能不够完美。
答案 11 :(得分:0)
检查指针。猜测,你正在取消引用一个空指针。
答案 12 :(得分:0)
当有一些对已删除对象的引用时,我发现'随机'崩溃了。由于内存不一定被覆盖,在许多情况下你没有注意到它并且程序正常工作,并且在内存更新后崩溃并且不再有效。
只是为了调试目的,尝试评论一些可疑的'删除'。然后,如果它不再崩溃,那么你就是。
答案 13 :(得分:0)
使用GNU调试器
答案 14 :(得分:-1)