本节修复了很多内存和堆溢出的BUG,尤其是堆的向后溢出的BUG找了两天时间,因为当时还不知道有gflags.exe调试工具,手工查找BUG很费 时,另外为SDL游戏引擎模块添加了捕获鼠标移动和点击事件的...

    本节修复了很多内存和堆溢出的BUG,尤其是堆的向后溢出的BUG找了两天时间,因为当时还不知道有gflags.exe调试工具,手工查找BUG很费 时,另外为SDL游戏引擎模块添加了捕获鼠标移动和点击事件的功能,并使用该功能做了个图形界面的测试程序,还做了一个计算打的费用的命令行界面的小程 序。

    本节v0.0.24版本的源代码的下载地址为:http://pan.baidu.com/share/link?shareid=222790&uk=940392313  (此为百度云盘的共享链接地址) ,访问该地址可以看到四个文件:zengl_lang_v0.0.24_forXP.rar (XP系统下的vs2008解决方案和源代码),release_v0.0.24_forXP.rar (此为可以在没装任何开发工具的XP系统下运行的发布版程式)zengl_language_v0.0.24_forLinux.tar.gz  (Linux系统下的源代码和makefile) ,v0.0.24-v0.0.23-diffs.txt  (v0.0.24和v0.0.23两个版本之间的代码变化情况)。

    从上面可以看出,本节多了个
release_v0.0.24_forXP.rar的压缩包,加压后可以看到如下界面:

    即使你的电脑没装Visual Studio之类的开发工具,也可以运行,因为上图中的zenglrun.exe等都是发布版的,MouseEvent.zl是测试SDL鼠标事件的脚本文件,MouseEvent.zlc是通过点击<编译MouseEvent.bat>批处理后生成的zengl脚本汇编代码文件,在生成MouseEvent.zlc后(压缩包中已经默认生成好了),直接双击<测试SDL鼠标事件.bat>即可运行:

    Taxi.zl则是计算打的费用的脚本文件,Taxi.zlc则是<编译Taxi.bat>批处理生成的汇编代码文件,双击<计算打的费用.bat>,可以看到结果如下:

   如无意外,这两个程序应该也可以在win7下直接运行,请自己尝试。当然linux下就只有将linux压缩包里的代码编译后才能运行了(Linux可不支持windows的可执行文件哦)。

    SourceForge.net上的仓库地址为:
https://sourceforge.net/projects/zengl/files/   从里面可以看到各个版本的代码压缩包,比如本节的zengl_lang_v0.0.24_forXP.rar release_v0.0.24_forXP.rarzengl_language_v0.0.24_forLinux.tar.gzv0.0.24-v0.0.23-diffs.txt

    先来看下本版本的描述(在linux代码包里的usage.txt里有这段描述,在目前几个带有git版本的....-diffs.txt和git log中也有这段描述):

    v0.0.24版本,该版本修复了一些BUG,并完成了利用SDL游戏引擎捕获鼠标事件的图形界面的示例脚本,还写了一个计算打的费用的命令行的小程序。

    该版本修复脚本编译时堆访问向后溢出的BUG,在parser.c的添加子节点函数和global.h定义的宏中有些地方没有对nodenum小于0的情况作处理就会发生向后溢出。
    还修复了ASM_ARRAY_ITEM对内部的节点产生的错误影响,添加了bltConvToInt函数将浮点,字符串等转为整形,添加了 sdl_21_point.zl 21点SDL游戏初步结构,为下一章节将要介绍的图形界面的21点扑克牌游戏作准备(该脚本在本节并没提供,将在下一章给出)。
    修复了run.c中op_addminis_one_array函数和op_set_array函数的段错误的BUG,在执行 memop_array_addr对数组元素进行操作前,先执行realloc_memblock函数根据index索引来扩展数组的内存空间,这样就不 会出现当index索引超出数组范围时写入非法内存的情况。这个BUG很难发现,一开始在开发21点图形界面时,每次调用sdl的打印文字的函数 TTF_RenderText_Shaded时都会出错,就是因为内存数据被破坏导致的(至于21点图形界面的脚本将在下一章节中给出)。
    另外扩展了sdlLib.c中的zl_sdl_drawtext函数,根据zengl脚本中sdlDrawText函数的参数分别调用 TTF_RenderUTF8_Solid和TTF_RenderUTF8_Shaded函数,这样就可以将zengl脚本中的utf8中文字符输出显示 出来。
    makefile文件里在SDL附加库部分添加了-lSDL_image , 这样才能使用IMG_load之类的SDL扩展库的函数来加载png之类的图片。
    本节的示例脚本一个是MouseEvent.zl(用于测试捕获鼠标事件的),一个是Taxi.zl(用于计算打的费用的小程序)
   
    其他的改动请用 git log -p 或 gitk 等软件查看。

 
    作者:zenglong
    时间:2012年6月16日
    官方网站:www.zengl.com
 
    上一节的v0.0.23版本的zenglrun在设置断点时会显示"原代码和原始版本不同,无法中断"的提示,原因是Visual Studio自身的BUG,这个BUG也找了蛮久,在run.c文件的开头有个char gl_FileFullPath[Max_PATH]={0};的代码,本版本在gl_FileFullPath和[Max_PATH]中间加个空格char gl_FileFullPath [Max_PATH]={0};就没提示了,网上也搜了些资料好像这是Visual Studio的一个BUG,在vs2010中也存在,估计只有微软自己知道是为什么了!

     另外以前的版本生成的Release版本的zengl_lang_v0.0.XX.exe在编译zengl脚本时都会报
"Free Heap block AXXXXXXXX modified at BXXXXXXXX after it was freed"的错误,这是堆溢出错误,意思是程序修改的堆空间在之前已经被释放掉了(释放掉的内存空间是不可以进行写入操作的), 但是这个错误在写入的时候并不会弹出来,因为如果在每次写堆空间时都进行堆检查那么程序的系统的开销就会变的很大,一般只有在free,或者malloc 操作甚至是fclose操作时,当要释放堆空间或重利用堆空间时,系统才会进行堆的检查,所以这个错误就只有在free或malloc或realloc之 类的函数中才会弹出,但是显然写入操作并不是在这些函数中进行的,所以导致这个BUG找了很久,最后通过在BXXXXXX内存位置设置数据断点来查看当这 个内存改变时中断程序才找到执行写入的代码位置(一开始找错了方向,在AXXXXX上下断点,所以找了半天,这个AXXXXX内存只是heap block堆块的起始地址,一般一个heap block有4096个字节也就是内存中的一页大小)。
 
 
/*将child对应的节点加入到parent节点的子节点中。*/
void AddNodeChild(int parent,int child)
{
    NodeChild_Type *childs  = &AST_nodes.nodes[parent].childs;
    Node_Type * nodes = AST_nodes.nodes;
    if(childs->count < NODECHILD_SIZE)
    {
        if(childs->childnum[childs->count] == -1)
            childs->childnum[childs->count++] = child;
        if(child >= 0 && child <= AST_nodes.count-1) //如果没有这条判断,那么当child为-1时就越界了,这个BUG很难查的!!因为他不会在越界的时候告诉你,而是在其他的稀奇古怪的地方提示,要设置内存数据断点,反复调试才能找出来。
            nodes[child].parentnode = parent; //设置child节点的parentnode字段为parent.
        return;
    }
.............................//省略N行代码

    上面当AddNodeChild函数的child参数为-1时如果没有if(child >= 0 && child <= AST_nodes.count-1)做判断,那么nodes[child].parentnode = parent;就会写入到nodes数组的-1索引的位置,nodes是在堆中动态分配的内存块,也就是会写入到内存块之前的位置,这样就发生了向后溢出,在写入时并不会中断,所以比较难找。
    当然对于这样的BUG可以使用gflags.exe工具进行调试,这个工具是Debug tools for windows工具集中的一部分,我将它也放在百度盘中,下载地址为:
http://pan.baidu.com/share/link?shareid=224174&uk=940392313 ,下载后安装该程序,该程序会再从网上下载完整版的安装程序然后自动安装,在完整版的安装目录中就有gflags.exe工具,下面介绍下该工具如何查找堆溢出BUG:

    在cmd下cd进入安装目录,然后输入 gflags.exe /p /enable xxxx.exe /full 这样就可以对xxxx.exe进行向前堆栈溢出的检查,如定义了一个数组int test[10]={0};如果test[12]=0; 这时visual studio就会立刻在这个位置中断并提示内存写入异常,因为test数组只有10个元素,但test[12]却访问到第13个元素,所以属于内存溢出, 如果要检查向后溢出,就需要使用gflags.exe /p /enable xxxx.exe /full /backwards命 令,多了个/backwards参数,如刚才的int test[10]={0};如果此时有段代码是test[-1]=0;那么visual studio就会在此中断。注意:向前溢出检查不可以同时检查向后溢出,所以你需要执行两次,一个向前溢出检查,一个向后溢出检查,才能判断你的程序是否 完全没有溢出,在使用gflags工具时,笔者就犯了这个错误没使用/backwards参数,所以就没检查出向后溢出的BUG。

  如果没有gflags进行设置,那么就无法在写入溢出时中断,就只有在其他的malloc,free,realloc之类的函数中才会检测到。

    本节的测试脚本MouseEvent.zl和Taxi.zl里都有详细的注释,如Taxi.zl:

use builtin;

//下面的计算方法是2012年的计算公式,此计算公式不包括遇到红灯时的费用
kilometer = '';
for(;;)
    printf('请输入长沙打的公里数(按q键退出):');
    kilometer = read();
    if(kilometer == 'q')
      break;
    endif
    kilometer -= 0.0; //转为浮点数
    money = 7.0; //晚上打的起步7块
    if(kilometer <= 1) //如果小于1公里,则3元/每公里
      money += 3 * kilometer;
    elif(kilometer <= 3) //三公里内每500米1元
      money += 3 + 2 * (kilometer - 1);
    elif(kilometer <= 10) //三公里外,每500米0.9元
      money += 3 + 2 * 2 + 0.9 * 2 * (kilometer - 3);
    else  //十公里外,再附加50%的返程费
      money += 3 + 2 * 2 + 0.9 * 2 * (kilometer - 3);
      money += money / 2; //50%的返程费
    endif
    print '(2012年计算公式得出)你的总打的费用:' + money + '';
endfor

    最后还是老生常谈的话题:
    windowsXP压缩包中的代码
都是采用GBK的编码,Linux压缩包中的代码以及git里的信息都是UTF8的编码,
还有本节的测试脚本MouseEvent.zl是采用UTF8的编码的,XP和linux下都是一样,因为SDL游戏引擎只支持UTF8的汉字显示而 Taxi.zl在XP压缩包中是GBK的因为XP的CMD只能显示GBK的编码,Taxi.zl在Linux压缩包中是UTF8的编码,因为linux终 端一般是显示UTF8的,所以如果哪些地方出现了乱码,请自行调整(像EditPlus,UltraEdit,linux下的kwrite等都可以调整编 码)。

    对于windows用户,请确保在项目属性的配置里,命令行参数配置的是MouseEvent.zl(或者是Taxi.zl)(对于zengl_lang_v0.0.24的项目)或
MouseEvent.zlc(或者是Taxi.zlc)(对于zenglrun的项目)。另外工作目录设置为../zengl_lang_v0.0.24  ,这样在zengl_lang_v0.0.24下修改了.zl脚本并重新生成.zlc后,就无需再将.zlc拷贝到zenglrun项目下了。

    另外对于vs2008的用户,我在项目属性里:[配置属性>>>>C/C++ >>>> 高级] 部分设置了禁用特定警告:4013,4715,4996 ,这几个警告会显示一些某某函数是非安全的函数,或者函数没有返回值等,这里禁用掉,防止出现过多的警告。另外还有个警告是显示某某变量没被使用过的, 这个警告我没禁用,可以不用管它。我最开始是在Linux系统中使用eclipse+CDT插件以及gcc等开发的zengl ,在我的GCC下面并没有显示过这些讨厌的警告,所以就没处理,不过还好这些警告都无关痛痒,无需理会。
    还有一个地方:VS2008项目中,在[配置属性>>>> C/C++ >>>> 预处理器] 部分都设置了预处理器定义的宏:OS_IN_WINDOWS,另外在zenglrun开启命令行调试配置中还增加了__ZENGL_DEBUG的宏,主要针对zenglrun的项目 ,之所以需要OS_IN_WINDOWS,是因为源代码既要在WINDOWS下编译,又要在LINUX下编译,所以需要通过这个宏来告诉编译器当前的环境 是windows还是linux,在 windows下面,在程序结束时会执行system ("pause");这条语句(vs2008下为了能看到结果,需要暂停,否则就一闪而过,什么都看不到咯。) 而linux系统主要在bash终端下执行,不需要这条语句,而__ZENGL_DEBUG宏主要用来判断是否需要在zenglrun的目标程序中加入调 试代码。
    本节还增加了Release版本,Release版本的程序是采用/MT多线程运行时编译的,所以可以直接在别的没有安装调试工具的电脑上运行。

    linux系统下的用户请结合usage.txt的说明,先运行make clean 将原来生成的zengl zenglrun zengldebug和 main.o parser.o assemble.o ld.o func.o run.o debug.o symbol.o builtin.o sdlLib.o文件
删除。

    再运行make all (单纯的make只能生成zengl,所以需要make all来生成所有的目标)

    生成zengl zenglrun
zengldebug 和 main.o parser.o assemble.o ld.o func.o run.o debug.o symbol.o  builtin.o sdlLib.o。(在生成过程中如果出现一些警告,暂不管他)

    最后运行 ./zengl
MouseEvent.zl(或者是Taxi.zl)
查看printASTnodes函数打印抽象语法树节点的结果,以及符号表输出的变量信息以及函数信息还有
类定义和类成员信息等。(例如变量的内存地址,以及在源文件的行列号,函数的唯一标识ID等)。
    接着运行
./zenglrun MouseEvent.zlc(或者是Taxi.zlc)

    如果要调试脚本则使用./zengldebug MouseEvent.zlc(或者Taxi.zlc)注意是.zlc结尾的文件名,因为zenglrun虚拟机和zengldebug调试器只能运行.zlc里的汇编代码)。

    zengl语言涉及到的很多高级的编译原理都可以在《龙书》中找到。

    最后的最后,如果转载请注明来源 http://www.zengl.com   , OK , 先到这里,休息,休息一下 O(∩_∩)O~

 

上下篇

下一篇: zengl编程语言v1.0.0图形版21点扑克游戏,do,while循环结构

上一篇: zengl编程语言v0.0.23,SDL游戏开发,类定义

相关文章

zengl v1.2.5 RC4加密,64位系统及Mac系统编译,Api调用位置规范,内建模块函数

zengl编程语言v0.0.9流程控制语句的实现

zengl编程语言v0.0.11实现循环控制结构

zengl编程语言v0.0.17单行多行注释

zengl编程语言v0.0.5构建符号表汇编代码和虚拟机

zengl编程语言v0.0.3构建抽象语法树