在C++中接受语法

ami*_*mit 0 c++ compiler-construction grammar

这是我坚持的实验室任务.

我需要接受这个语法(ab)*b,它基本上意味着任何数量的"ab"并以b结尾.

我编写了这段代码但不知何故,它只检查前2个字母.

#include <iostream.h>
#include <conio.h>
#include <string.h>

enum track {true, false};

void main()

{
    clrscr();
    char*str;
    enum track track_pos, track_pos_2;
    cout<<"enter the string: ";
    cin>>str;
    int len=strlen(str);
    cout<<"length of the string is "<<len;
    getch();
    int i;
    for(i=0;i<len; i++)
    {
        ++str;
        cout<<"loop"<<i;
        if(*str=='a' && i%2==0)
        {
            cout<<"\nchecking a...";
            track_pos=true;
            cout<<"\na.check";
            ++str;
            if (*str=='b')
                {
                cout<<"\nchecking b...";
                track_pos=true;
                cout<<"\nb.check";
            }
            else{
                track_pos=false;
                cout<<"\nb.uncheck";
            }
        }

    }

    if(*str=='b')
        track_pos_2=true;
    else
        track_pos_2=false;

    if(track_pos==true && track_pos_2==true)
        cout<<"\nThe string is accpeted.";
    else
        cout<<"\nThe string is rejected.";

    getch();
    cout<<"\n\nDo you want to continue (Y/N)? ";
    char ch;
    cin>>ch;
    if(ch=='y' || ch=='Y')
        main();

}
Run Code Online (Sandbox Code Playgroud)

180*_*ION 12

我会后悔这一点,但每次看到这个问题,我都会看到你的代码出现了其他问题.这是逐行的.我可能错过了很多.

此标头的正确名称是"iostream",而不是"iostream.h" - 不推荐使用".h"版本.类似地,在现代C++中使用"string"而不是"string.h",并使用现代STL字符串类.

#include <iostream.h>
#include <conio.h>
#include <string.h>
Run Code Online (Sandbox Code Playgroud)

正如所指出的,不要这样做.您已将标准bool类型重新定义为具有与标准类型相反的值.我甚至都不知道这是合法的.

enum track {true, false};
Run Code Online (Sandbox Code Playgroud)

main函数的返回值int不是void.

void main()    
{
    clrscr();
Run Code Online (Sandbox Code Playgroud)

你知道缓冲区溢出是什么吗?你已经str在这里定义了一个指针,没有分配内存,稍后你会写入那个未定义的内存位.这是未定义的行为,您几乎可以保证崩溃.我建议,你应该定义strstd::string- 这将很好地避免缓冲区溢出,它有许多有用的方法,你可以在你的程序中使用.

    char*str;
    enum track track_pos, track_pos_2;
    cout<<"enter the string: ";
Run Code Online (Sandbox Code Playgroud)

这是缓冲区溢出就在这里.你在写谁知道什么是记忆区.

    cin>>str;
Run Code Online (Sandbox Code Playgroud)

如果strstd::string- 你会这样做size_t len=str.length();

    int len=strlen(str);
    cout<<"length of the string is "<<len;
Run Code Online (Sandbox Code Playgroud)

将这样的控制台IO功能与iostreams功能混合起来可能不是一个好主意 - 有一些缓冲问题会导致困难.

    getch();
Run Code Online (Sandbox Code Playgroud)

声明i在循环体中,因为你没有再使用它.像这样:

    for (int i=0; i<len; i++) etc...

    int i;
    for(i=0;i<len; i++)
    {
Run Code Online (Sandbox Code Playgroud)

而不是使用poiter算法,因为你要跟踪当前字符的索引i,只需使用它并str视为一个数组.这样,您就不必stri所有方式保持同步.顺便说一下,这是导致您报告错误的原因.

        ++str;
        cout<<"loop"<<i;
Run Code Online (Sandbox Code Playgroud)

你应该改为:

        if (str[i]=='a' && i%2==0)
Run Code Online (Sandbox Code Playgroud)

(与指针算术版本不同,即使strstd::string顺便说一句也是如此).

        if(*str=='a' && i%2==0)
        {
Run Code Online (Sandbox Code Playgroud)

你真的应该在某个时候辍学,如果你发现字符串不匹配,那么就没有任何意义继续到字符串的末尾.

                cout<<"\nchecking a...";
Run Code Online (Sandbox Code Playgroud)

我不赞成这样的状态标志 - 由于这些标志的激增,你的代码部分难以理解,你无法跟踪正确的行为.这个名称track_pos不是助记符,如果没有详细的代码研究,很难弄清楚其意味着什么.

我建议你在for循环体内重构你的代码来调用一个函数,其目的只是匹配一组"ab" - 如果这样做,这个函数可以返回true,如果是,则返回false没有.

                track_pos=true;
                cout<<"\na.check";
Run Code Online (Sandbox Code Playgroud)

请注意,由于我们正在处理之前提到的缓冲区溢出,因此您将迭代未定义的内存.另请注意,您没有i在这里增加.

                ++str;
                if (*str=='b')
                        {
                        cout<<"\nchecking b...";
                        track_pos=true;
                        cout<<"\nb.check";
                }
                else{
                        track_pos=false;
                        cout<<"\nb.uncheck";
                }
        }

    }
Run Code Online (Sandbox Code Playgroud)

当我们到达这里时,根据你的for循环,我们迭代了整个字符串,所以我们必须看过字符串的末尾(甚至忽略缓冲区溢出),所以这个测试没有可能成功.简而言之,你的for循环必须走得太远.

    if(*str=='b')
        track_pos_2=true;
    else
        track_pos_2=false;

    if(track_pos==true && track_pos_2==true)
Run Code Online (Sandbox Code Playgroud)

我应该提一下拼写错误吗?

        cout<<"\nThe string is accpeted.";
    else
        cout<<"\nThe string is rejected.";

    getch();
    cout<<"\n\nDo you want to continue (Y/N)? ";
    char ch;
    cin>>ch;
Run Code Online (Sandbox Code Playgroud)

如果将代码重构为适当的子例程,您将发现程序的结构可以自行处理.请注意,main递归调用并非严格违法,但它有点奇怪并且有一个明显的漏洞,如果程序永远不会退出,将导致最终的堆栈溢出.

    if(ch=='y' || ch=='Y')
        main();

}
Run Code Online (Sandbox Code Playgroud)

  • "将导致最终堆栈溢出的漏洞" - 他的程序已经导致他堆栈溢出.点com. (5认同)

Zed*_*Zed 6

实现一个简单的状态机.它有以下状态:

  • 0 =开始
  • 1 ='收到一个(ab)'
  • 2 ='收到b(ab)'
  • 3 ='收到最后一个b'
  • -1 =错误,语法无效

那么你只需要一个这样的函数:

int nextState(int currentState, char inputChar) {
  if (currentState == 0 && inputChar == 'a') return 1; // handled string is "a"
  if (currentState == 0 && inputChar == 'b') return 3; // handled string is "b"
  if (currentState == 1 && inputChar == 'b') return 2; // handled string is "ab", or "abab", or ...
  if (currentState == 2 && inputChar == 'a') return 1; // handled string is "aba", or "ababa", or ...
  if (currentState == 2 && inputChar == 'b') return 3; // handled string is "abb", or "ababb", or ...
  return -1;
}
Run Code Online (Sandbox Code Playgroud)

在输入字符上迭代这个"状态机",从状态0开始,如果你在状态3结束,你的输入是有效的.

int isValid(char* inputString) {
  int state = 0;
  for(int i=0; i<str_len(inputString); i++) {
    state = nextState(state, inputString[i]);
  }

  return (state == 3);
}
Run Code Online (Sandbox Code Playgroud)

  • 对不同的状态使用枚举,这将使代码更易读和可维护.在开始在纸上设计状态机的编码之前,要确保涵盖所有不同的状态转换. (3认同)