easyX作为一个第三方图形库,出现问题时问题的原因往往不容易找出来。比如一个图形不移动的时候,可能是链表出了问题,也可能是图像输出部分参数有错误,如果漫无目的的一次次看源代码,可能要很久才能找出原因;又因为第三方图形库往往不能方便的分步调试,这时候一个合理的调试方法就变得非常重要。
不同的程序中,所需要的调试方法各不相同,一个合理的调试方法应该可以帮助找到问题的发生点,最好能独立于程序的运行输出调试结果,可以保留程序出错的轨迹。
虽说easyX不能直接使用调试工具进行调试,但easyX本身在设计的时候给我们留下了很多可以使用的调试手段。下面就来结合昨天晚上的实例说一说这些调试方法的具体应用,希望可以给做课设的同学们打开一些思路。
最简单的断点调试
断点调试设置最简单,不需要添加任何语句,可以结合IDE的功能做一些简单的判断,比如当程序无法正正常执行但又不报错时,可以通过断点观察变量的值,或者观察程序是否陷入死循环无法执行到某一行。
如上图所示,当我拿到程序之后,程序不报错,但是不能正常运行,代码结构看起来问题又不大,因为是别人的代码,从头看代码缕清思路又不现实,所以我做的第一步就是在程序运行的主循环中各个函数设置断点。
当我将断点设置在getMOVE函数中时,断点无法触发,说明程序无法执行到getMOVE这一行,在之前的函数中陷入了死循环。
而当我将断点设置到fishmove中时,断点触发,说明死循环位于fishmove函数中,接下来我就可以有针对性的解决这个函数中的死循环问题,解决之后更便于从整体考虑问题。
使用命令行调试
使用命令行调试是使用easyX调试程序的最好的方法。比起观察图像的变化,使用命令行调试可以像写普通程序一样,使用printf直接输出其中的变量以变量的值,更直观且更精确,而且很多图像无法反映出来的数据,只能通过命令行进行输出。
如何使用命令行输出
在初始化绘图环境的时候,使用如下语句进行初始化:
initgraph(640, 480, SHOWCONSOLE);
即在坐标后加参数SHOWCONSOLE,即可在显示图形界面的时候保留命令行窗口,接下来就可以使用printf等语句进行调试。
下面举几个使用命令行调试的例子,供大家打开思路,来源均来自于昨天调试的代码:
判断链表是否操作正常
链表让很多同学头疼,尤其是在图形界面下原因难以判断的时候更是如此,当其他部分还存在问题时,只通过图形界面根本无法判断链表是否正常工作,这个时候就可以使用命令行来独立观察链表工作情况。
昨天我调试的代码中,图像显示不正常,我要测试一下函数是否正常遍历了链表中的所有元素。
于是我在appear函数遍历链表的循环体中,设置了标记变量,并设置了相应的输出语句,代码如下:
运行结果如下:
在初始化的时候,代码初始化了十五个对象,这里函数共输出了15次,考虑到边界处理等因素带来的输出错误,数量基本没有错误,说明链表被正常遍历。
同样的方式我还用来测试了移动所有电脑控制的鱼的fishmove函数:
fishmove函数运行的次数和appear函数输出的次数一样,说明在遍历链表的操作上,二者没有错误。
注意点
因为我们在命令行输出内容是为了方便调试,因此我们要能通过输出的内容判断出该内容来自于哪个一函数的哪一部分。因此有一些注意点:
一定要换行。一条信息占一行才能让你更清晰的看清楚每一条信息内容。
要标记函数名。比如inappear, in fishmove等,不要自己猜测该变量来自于哪一个函数。
测试循环的时候要注意输出循环数量。
如果输出变量,要记得写上变量名,比如ptinf(“data=%d\n”,%d),方便观察。
判断某一个条件是否执行
这一点也比较常见。当你写了一个按键操作的代码段,但你的操作并没有对程序产生影响,这是你如何判断该操作是否被程序捕捉?这个时候用命令行也可以解决。
在昨天的程序中,我看到了一个我没用过的获取按键消息的函数GetAsyncKeyState(如下图所示):
此时,我无法通过按键控制鱼的移动。虽说我可以很轻松的查到函数的功能,但我无法确定函数的触发方式在游戏中能否实现。
这时,可以通过命令行来判断按键是否能正常操作:
如果按下W的时候,命令行可以输出in getMOVE GetAsyncKeyStateW,则说明该条件语句可以执行,问题不出在这里。运行一下之后结果如下:
如上图所示,命令行窗口输出了该语句,说明按下W的时候,条件语句正常执行,问题是在别的地方。
测试变量是否被修改
同样的道理,我们需要测试某些变量的时候,我们也可以直接在命令行输出。比如测试坐标是否被修改。
当我们遍历修改链表中的元素的时候,我们只需要测试其中某一个元素的坐标是否被修改即可判断程序是否正常执行。因此我们可以只在计数到某个固定的值的时候,输出该元素的坐标值,如下图所示:
运行的时候如下图所示:
从284到302,说明坐标值被修改。
总之,善用命令行让你方便地独立调试课设中某一部分的内容,对某些中间变量等进行输出,可以让你快速找到程序中的问题。
使用ifdef切换调试模式
使用ifdef/endif语句,可以让编译器在编译程序的时候,根据是否定义某一段宏,来决定是否编译该段语句。
上图中,当宏定义中使用#defineDEBUGMODE定义了DEBUGMODE之后,其中的语句可以运行;如果取消对DEBUGMODE的定义,比如将宏定义注释掉:
可以发现上面的语句变为灰色:
此时再编译,该段语句不会执行。
合理使用该功能,比如将所有的测试语句全部至于ifdef/endif之中,有两个好处:
可以通过是否注释掉定义的宏(如DEBUGMODE)来关闭/开启所有的测试语句;
当你的测试语句非常多的时候,可以通过设置多个不同的宏定义,开启/关闭不同的测试程序,避免过多的测试语句造成干扰。比如定义MOVEMODE,JUDGEMODE等,根据不同的情况开启不同的测试语句。
关于图片读取
这一点和测试无关。
easyX在读取图片文件的时候会消耗大量的时间,当你的程序运行非常缓慢的时候,很可能就是因为你进行了重复文件读取操作。
当你在函数中使用了读取函数功能时,可以使用下面的方式使该函数在程序运行的时候无论调用多少次,都只进行一次文件读取操作,提高程序的执行效率:
将储存图片的指针(FISH)设置为static静态变量,并设置一个静态标记变量(loadFlag),读取一次文件之后,修改标记变量,下一次便不再进行读取操作。
关于调试的内容就到这里
希望这些内容可以帮助做课设的同学们更快更好地完成自己的课设
如果有需要补充或者有其他疑问的可以后台联系我或者在下面留言
之前我还写过一篇输出透明图片的解决方案,欢迎点击查看:
C语言课设中调用EasyX库输出透明图片的方案(含图片处理方法&实现代码)