打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
《话说程序调试》摘录1
《话说程序调试》摘录1 浏览:176
  由清华大学出版社出版的、当当网可以购买的《话说程序调试》一书,对于学习编程的人员是有帮助的新书。虽然该书以C语言举例,但所讲述的内容同样适用其他语言。为了与大家分享,斗胆摘录其中部分内容,供大家参考。

2.1语法错误及其排查
2.1.1.相关语法分析知识
编译程序所处理的原始数据是源程序,程序设计语言所编写的源程序可以看做一个(文本)字符串,编译程序对源程序的编译,通常经历词法分析、语法分析、语义分析、中间代码生成和中间代码优化、目标代码生成这些步骤。词法分析的任务是从源程序中识别(分离)出一个个单词(又称为符号)、并将单词转换为内部表示(TOKEN码)。所谓单词,是独立的最小语义单位,如源程序中的保留字、标识符(如变量名、函数名等)、常量、运算符等。在识别单词时,分界符(界限符)是帮助分离单词的重要符号。语法分析阶段则是根据语言的语法规则对单词的内部表示(TOKEN码)序列进行语法检查和分析,检查各个语句是否符合语言的语法规则,是语法检查主要任务之一,语句是语法分析的基本单位。当发现一个错误之后,为了能使编译程序对源程序中后续的语句继续进行语法分析,都采用某个策略进行错误恢复。常用的错误恢复策略有:
(1)  紧急方式恢复。做法是,发现错误时,分析器每次抛弃一个输入符号(单词),直到当前输入符号属于某个指定的同步符号集合为止。同步符号一般是定界符。
(2)  短语级恢复。发现错误时,分析器对剩余输入做局部纠正,用可以使分析器继续分析的符号串代替剩余输入串的前缀。如用分号代替逗号、删除多余的分号、插入遗漏的分号等。
(3)  出错产生式。若编译器的设计者对经常遇到的错误了解得非常清楚,可以通过扩充语言的文法,增加产生错误结构的产生式,当遇到的错误被出错产生式所识别时,可以产生适当的错误分析信息。
较多的编译器采用错误恢复策略(1)或(2)对语法分析中出现的错误进行恢复。
前已述及,编译程序报告的语法错误主要有两部分内容:错误位置(所在程序行)和错误原因,因此,编译器的出错信息对于语法错误排查可能有如下四种情形:(1)错误原因不准确、位置准确;(2)错误原因准确、位置准确;(3)错误原因准确、位置不准确;(4)错误原因不准确、位置不准确。下面就这四种情形,分别示例探讨语法错误排查规律。
2.1.2. 位置准确、原因不准确
一些语法错误的出错信息中所报告的出错位置还准确、但致错原因却不太准确,这类情形下,寻找错误是容易的(根据出错信息就找到)、而改正错误却需要进行必要的分析。

例 2.1 需求描述:输入圆的半径,计算输出圆的面积。
程序:
/*ch2_ex1.c*/
#define pi 3.14
main()
{
 float r,circle-area;  /*变量名circle_area中的下划线误敲为减号、下同*/
 printf("Enter radiue r\n");
 scanf("%f",&r);
 circle-area=r*r*pi;
 printf("The circle area=%f\n",circle-area);
}
编译调试:
程序编译后,编译程序给出的错误报告信息如下:
Error ch2_ex1.c:4 :Declaaration syntax error in function main
Error ch2_ex1.c:7 :Undefine symbol ‘area’ in function main
Error ch2_ex1.c:7 Lvalue required in function main
Warning ch2_ex1.c:87: Possible use of ‘circle’ before definition in function main
在此例,编译程序并没有将不符合命名规则(词法)的变量名circle-area作为词法错误对待,而是由于识别单词时将circle-area识别为circle和area,导致该语句出现语法错误。同时可见,这里发生错误的原因,本来是因为变量名的命名不符合语言的词法规则,但错误信息却报告为变量声明错误、变量area没有定义、变量circle在使用之前没有赋初值,显然,如果根据编译程序报告的错误信息查找错误可以容易定位错误,但改正错误则需要理清程序设计时的原意,然后改正错误。
值得一提的是,一般的编译程序采用识别单词的方式从源程序中识别(分离)单词。就象阅读英文文章,我们以以空格和标点符号来分隔单词,以本例来说,对变量定义语句
float r,circle-area;
我们可能根据语句中的空格和逗号,看出被说明的变量有r和circle-area。但是,当编译程序从左向右逐个扫描字符、识别单词时,可以根据C语言词法识别出float、r,再连续读取到circle后遇到(非标识符组成符号的)减号(-),于是就将circle当作一个标识符、并将减号作为一个(特殊符号)单词,此后再将area(分号为定界符)识别为标识符。于是,一个不符合语言词法规则的单词circle-area就被识别为两个单词(标识符),而在此后的语法分析阶段,则因为circle与area之间的减号不符合C语言的语法规则,查出了该语句的语法错误。因此,一些词法错误可能不被编译程序当作词法错误发现和报告,而是在此后的语法分析阶段当作语法错误处理,因此,一般的编译程序并不单独报告源程序中的词法错误。
例 2.2 需求描述:求a=1+2+……n,若a为偶数则输出相应信息。
程序:
main()  /*ch2_ex2.c*/
{
  int i,n,a=0;
  printf("Input integer n:\n");
  scanf("%d",&n);
  For(i=0;i<=n;i++)  /*保留字for的f大写了*/
   {
     a+=i;
     if(a%2==0   printf("sum is even\n");
   }
}
编译调试:
编译程序给出的错误信息如下:
Error ch2_ex2.c 6:Function call missing ) in function main
Warning ch2_ex2.c 6:code has no effect in function main
Warning ch2_ex2.c 6:possible use of ‘i’ before definition in function main
Warning ch2_ex2.c 6:possible use of ‘i’ before definition in function main
Error ch2_ex2.c 6:Statement missing ; in function main
Warning ch2_ex2.c 11:’a’ is assigned a value which is never used in function main
我们看到,编译程序分析出的错误信息有6条,其中,有5条错误信息定位在第6行(警告错误4条),编译程序分析的语法错误原因分别为:函数调用缺少右括号;代码无效;语句缺少分号。但实际情况是,源程序中仅仅因为循环语句保留字for的字母f错作大写。显然,编译程序所显示的出错信息报告的语法出错原因与实际致错原因并不贴切,我们刻板地根据提示信息查找错误,将可能被误导。另一方面,前5条出错信息指定的错误位置(错误所在的源程序行号)却是正确的,如果我们对语言的词法和语法规则熟悉,就可以从该行的语句发现错误并加以改正。改正后重新编译一次,如果不再出现错误信息,说明错误已得到改正;如果还有错误报告信息(见例2.4),再根据当前的错误报告信息查找错误和改正错误。
为什么会将语句中的一处错误当作这么多的语法错误呢?这是因为编译程序在出现错误后进行错误恢复、以及语句的上下文相关性造成了错误点的增加。这也说明,编译程序报告多少个语法错误,不一定就有多少个实际的错误存在、并需要加以纠正。因此,对待编译程序报告的出错信息,我们不能刻板地以该信息去查找所指定的错误,而应以它作查找错误的参考,并结合所学的词法规则和语法规则,通过分析发现错误;并且,最好一次确定和改正一个语法错误、再次编译后根据新的编译错误信息查找下一个错误。无论如何,当编译程序报告了出错信息,就说明源程序中一定存在错误需要改正。

2.1.5.原因不准确、位置不准确
对于查找语法错误最特殊的情形是,编译程序报告的错误信息中,出错位置和出错原因均不准确,此时,也需要从报告的第一个错误的位置,倒着向回(回溯)逐行查找错误,并且需要熟悉算法、通过语法分析确定错误。

例2.8需求描述:老鼠走迷宫程序(函数mazepath摘自参考文献[6]P30的C语言算法,仅改写了形参、增加了局部变量定义等,并且省略函数init_array和disp_path的代码,main函数中也注释掉相关调用语句,使得能单独编译)。
程序:
/*ch2_ex8.c*/
#include <stdio.h>
#define MAX_SIZE 30
typedef struct stk_elm  /*定义堆栈元素*/
   {
     int i,j;
     int d;
   }stk_elm;
stk_elm stack[MAX_SIZE]; /*存储路径的堆栈*/
int mark[MAX_SIZE][MAX_SIZE];/*访问标志数组*/
int top,maze[MAX_SIZE][MAX_SIZE];/*栈指针top为全局量,迷宫矩阵*/
void mazepath(int move[2][4],int m,int n)
{
  int i,j,d,g,h;
  mark[1][1]=1;
  top=0;
  i=1;
  j=1;
  d=0;
  do
    {
     g=i+move[0][d];
     h=j+move[1][d];  /*try*/
     if((maze[g][h]==0)&&(mark[g][h]==0))
  {
    mark[g][h]=1;  /*Enter new pos*/
    top++;
    stack[top].i=i;  /*old pos push*/
    stack[top].j=j;
    stack[top].d=d;
    i=g;
    j=h;
    d=0;
  }
     else {
      if(d<3) d=d+1;
        else
    {
      if(top>0)
        { i=stack[top].i;
          j=stack[top].j;
          d=stack[top].d;
          top--;
        } /*try again*/
        else printf("No path has been found\n");
    }while((g==m)&&(h==n));/*do-while*/
    printf("Out maze\n"); /*走出迷宫*/
}
main()
{
 int movd[2][4]={0,1,0,-1,1,0,-1,0};  /*移动方向数组*/
 int i,j,row,col;
 printf("Input The Row and Col(15  11)\n");
 scanf("%d%d",&row,&col);
 /*init_array(mark,row,col,0); */
 /*init_array(maze,row,col,1); */
 printf("Input data(0 or 1) for row\n");
 for(i=1;i<=row;i++)
   for(j=1;j<=col;j++)
     scanf("%d",&maze[i][j]);
  mazepath(movd,row,col);
/* if(top>0) disp_path();*/
}
编译调试:
编译程序报告的出错信息如下:
Erroe ch2_ex8.c 50:Statement missing ; in function mazepath
Erroe ch2_ex8.c 65:Compound statement missing } in function mazepath
Erroe ch2_ex8.c 66:Do statement must have while in function mazepath
Erroe ch2_ex8.c 67: Compound statement missing } in function mazepath
从显示的出错信息看(编译器的错误指示光条指示50行),50行为main函数开始的左花括号,此处也不应该有分号。那么,出错信息表达的出错原因和位置均不准确,但错误是一定存在的。我们向回查找,发现38行的左花括号在此后没有右花括号匹配,并且,根据C的语法,38行的左花括号也可以省略。我们去掉38行的左花括号(删除该行),再编译,得到如下的编译信息:
Erroe ch2_ex8.c 48:Do statement must have while in function mazepath
Erroe ch2_ex8.c 64: Compound statement missing } in function mazepath
这说明,解决了一个错误,还存在错误,继续向回查找。发现35行的左花括号也为类似情形,去掉35行的左花括号,再编译一次,没有编译错误了。顺便说一下,这个程序尽管改正了语法错误,不一定没有逻辑错误。请耐心地看第4章的示例。
本例与例2.7虽然都是因为缺少配对的右花括号导致语法错误,但是,编译程序报告的出错信息却不同。原因仍然是编译程序的错误恢复所致,编译程序总是向前寻找最近的右花括号与左花括号匹配,于是,用46行的
}while((g==m)&&(h==n));/*do-while*/
的花括号与38行的左花括号匹配,从而使得do语句缺少了该有的while;以48行的花括号与35行的左花括号匹配,就使得49行的主函数头
Main()
成为函数mazepath的函数体语句,而该语句恰恰缺少语句末尾的分号。这就解释了编译程序显示的出错信息是怎么回事了。
显然,当花括号内包含的语句结构不同时,引起的错误也不同。也就是说,不同场合下缺少复合语句的右花括号,会导致不同的出错情形。
---------------------------------
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
《编译原理简明教程》PPT 第12章
使用命令编译VB.NET程序
编译原理 总结
程序设计语言:编译原理(第3版)
7016 编译原理.doc
高级语言程序的两种处理方式——编译和解释
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服