C程序设计语言(第二版):练习1-23

2021/10/27 11:11:00

本文主要是介绍C程序设计语言(第二版):练习1-23,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

题目:

编写一个删除c语言程序中所有的注释语句。要正确的处理带引号的字符串与字符常量。在c语言中注释不允许嵌套

首先需要理解什么是注释嵌套,c语言中有两种注释符,一种是行注释符//,一种是块注释符/**/

其中行注释符,一次只能注释一行,//之后的全部为注释

块注释符要成对出现,注释从/*开始到*/结束。

注释嵌套是指对块注释符所说,例如下面为注释嵌套的例子:

/* /* this is the comments */ */

注释从第一个/*开始,到第一个*/结束。所以最后一个*/不在注释范围内,这种注释是不被允许的,否则会导致编译错误

自我解答:

#include <stdio.h>

#define  IN    1
#define  OUT   0
int main()
{
    int c;
    bool comInStr = OUT;        //the comment symbol // or /* in the string
    bool blockCom = OUT;        // the character is in comment block "/**/"
    bool lineCom = OUT;         // the character is in a comment line "//"
    bool isComSymbol = false;          // if it is comment symbol "//" or "/*"
    bool outComBlock = false;   //the flag for out of comment block
    
    while((c = getchar()) != EOF)
    {
        if(comInStr)
        {
           putchar(c);       //output the content in " "
           if(c == '"')
              comInStr = OUT;  //end of the content in " "
        }
        else
        {
            if(lineCom == IN)
            {
                if(c == '\n')    // will not output until to the end of the line
                {
                    putchar(c);
                    lineCom = OUT;
                    isComSymbol = false;
                }
            }
            else if(blockCom == IN)
            {
                if(outComBlock)
                {
                    if(c == '/')
                    {
                        blockCom = OUT;       //find the end symbol "*/"
                        isComSymbol = false;
                    }
                    outComBlock = false;
                }
                else
                {
                    if(c == '*')
                       outComBlock = true;
                }
            }
            else
            {
                if(c == '"')
                {
                    comInStr = IN;     //found the string
                    putchar(c);
                    continue;
                }
                if(isComSymbol)
                {
                   if(c == '/')
                       lineCom = IN;
                   else if(c == '*')
                       blockCom = IN;
                   else
                   {
                       putchar('/');
                       putchar(c);
                       isComSymbol = false;
                   }
                }
                else
                {
                    if(c == '/')
                        isComSymbol = true;
                    else
                        putchar(c);
                }
            }
        }
    }
    return 0;
}

编程思路:定义布尔型变量lineCom和blockCom 分别表示当前是否在注释行和注释块之内。

顶层逻辑为:如果当前字符在注释行之内,则不输出字符,直至到行尾;如果当前字符在注释块之内,则不输出字符,直至遇到注释块结束符*/。通过识别“//”和“/*”来区分是否是注释行或注释块,在识别过程中需要忽略这两种字符在字符串常量之内出现

为上述程序写一个测试输入,尽可能包含各种情况:

#include <stdio.h>    //test line comment

//test line comment
/*
test block comment
define the line comment and 
block comment
*/
#define  LINECOMMENT    "//"      /* test // in string */
#define  BLOCKCOMMENT   "/*"      // test /* in string

int main()
{
    int a = 0; /*test comment between two statements*/ int b = 1;
    char str[] = "I am //, who are you"; //
    char str1[] = "I am /*, and you";  // "haha  I am here" /* */ */
    return 0;
}

输出如下:

#include <stdio.h>    



#define  LINECOMMENT    "//"      
#define  BLOCKCOMMENT   "/*"      

int main()
{
    int a = 0;  int b = 1;
    char str[] = "I am //, who are you"; 
    char str1[] = "I am /*, and you";  
    return 0;
}

从输出中可见,删除了注释部分并且保留了 字符串和字符常量中的 注释符号

参考答案:

#include <stdio.h>

void rcomment(int c);
void in_comment(void);
void echo_quote(int c);

/* remove all comments from a valid C program                   */
int main()
{
    int c, d;
    while((c = getchar()) != EOF)
        rcomment(c);
    return 0;
}

/* rcomment: read each character, remove the comments           */
void rcomment(int c)
{
    int d;
    
    if(c == '/')
        if((d = getchar()) == '*')
            in_comment();       /* beginning comment            */
        else if(d == '/')       
        {
            putchar(c);         /* another slash                */
            rcomment(d);
        }
        else
        {
            putchar(c);         /* not a comment                */
            putchar(d);
        }
    else if(c == '\'' || c == '"')
        echo_quote(c);          /* quote begins                 */
    else
        putchar(c);             /* not a comment                */
}

/* in_comment: inside of a valid comment                        */
void in_comment(void)
{
    int c, d;
    c = getchar();              /* prev character                */
    d = getchar();              /* curr character                */
    while(c != '*' || d != '/') /* search for end                */
    {
        c = d;
        d = getchar();
    }
}

/* echo_quote: echo character within quotes                     */
void echo_quote(int c)
{
    int d;
    
    putchar(c);
    while((d = getchar()) != c)
    {                           /* search for end               */
        putchar(d);
        if(d == '\\')
            putchar(getchar()); /* ignore escape seq            */
    }
    putchar(d);
}

这个程序假设输入是一个合法的C程序。函数rcomment搜索注释语句的起始标志(/*);在找到这个标志时,它将调用另一个函数in_comment搜索注释语句的结束标志(*/),从而确保C程序中的注释语句都能被删除。

函数rcomment还将搜索单引号和双引号;在找到它们时,它将调用另一个函数echo_quote。函数echo_quote的参数将指明找到的字符是一个单引号还是一个双引号。echo_quote确保引号中的内容能够按原样输出,不会被误认为是注释。函数echo_quote不会把跟在一个反斜杠后面的引号看做是结束引号(参见教材第13页和练习1-2中关于转义字符序列的讨论)。其他任何字符都将按原样输出。

本程序将在getchar返回文件结束符时结束运行。

补充:

参考答案中的思路比较清晰,可读性较强。但此程序也存在一些问题:

首先参考答案程序的前提必须是一个合法的C程序。

例如下面的输入:int main()   /*main function

被认为是一个不合法的程序,如果用这种输入,程序将会在in_comment中陷入死循环。而自我解答对这种输入并不敏感,当用同样的输入时,输出为:int main()    依然能把/*后面的注释去掉。

再例如下面的输入:#define  LINECOMMENT    "//,程序将在echo_quote中陷入死循环。自我解答依然可以正常输出。

其次参考答案程序只能处理块注释符即/**/的情况,对于行注释//不起作用。

例如用下面的输入int main()   //main function

程序不能识别//而原样打印出来,自我解答可以处理这种情况

从参考答案中可以借鉴的地方有两个:一是在rcomment里面的递归调用,一个是对转义字符的处理

自我解答中没有进行转义字符处理和单引号的处理,没有增加对单引号的处理是因为当时考虑,单引号定义的是单个字符不会出现注释符的情况。但是单引号内仍然可能会有转义字符的情况例如:'\''.在自我解答的程序进行这个功能的扩展也比较简单,如下即可:

int c, lastc;    增加lastc用于记录上次字符是单引号还是双引号

if(comInStr)
        {
           putchar(c);
           if(c == '\\')             //如果是转义字符打印下一个字符
           {
              putchar(getchar());  //print the next char
              continue;
           }

           if(c == lastc)        // 判断引号类型是否一致
              comInStr = OUT;     //end of the content in " "
        }

      在line61行增加   lastc = c;         // store ' or " in lastc    记录当前引号类型



这篇关于C程序设计语言(第二版):练习1-23的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程