词法分析器正确输出但指针“filename”包含错误的名称

时间:2016-02-09 14:55:58

标签: c regex stack flex-lexer

此代码的目的是读取以下txts(d.txt,e.txt,f.txt)并执行将字母顺序正确的字母放入output.txt所需的操作。代码假设工作,因为在output.txt我得到了正确的结果,但我使用printf进行的测试存在问题(它在newfile函数的末尾)。为了运行我输入d.txt和output.txt作为输入。 它应该打印

top->prev points to file :d
top->prev points to file :e

但是它会打印以下内容而我找不到原因

top->prev points to file :d
top->prev points to file :f

d.txt:

abc
#include e.txt
mno

e.txt:

def
#include f.txt
jkl

f.txt:

ghi

代码:

%{
#include <stdio.h>
#include <stdlib.h>

struct yyfilebuffer{
    YY_BUFFER_STATE bs;
    struct yyfilebuffer *prev;
    FILE *f;
    char *filename;
}*top;

int i;
char temporal[7];
void newfile(char *filename);
void popfile();
void create();
%}

%s INC
%option noyywrap
%%
"#include " {BEGIN INC;}
<INC>.*$ {for(i=1;i<strlen(yytext)-2;i++)
          {
            temporal[i-1]=yytext[i];
          }
          newfile(temporal);
          BEGIN INITIAL;
         }

<<EOF>> {popfile();
        BEGIN INITIAL;
        }
%%

void main(int argc,int **argv)
{
    if ( argc < 3 )
    {
        printf("\nUsage yybuferstate <filenamein> <filenameout>");
        exit(1);
    }
    else
    {
        create();
        newfile(argv[1]);
        yyout = fopen(argv[2], "w");
        yylex();
    }
    system("pause");
}

void create()
{
    top = NULL;
}

void newfile(char *filename)
{
    struct yyfilebuffer *newptr;
    if(top == NULL)
    {
        newptr = malloc(1*sizeof(struct yyfilebuffer));
        newptr->prev = NULL;
        newptr->filename = filename;
        newptr->f = fopen(filename,"r");
        newptr->bs = yy_create_buffer(newptr->f, YY_BUF_SIZE);
        top = newptr;
        yy_switch_to_buffer(top->bs);
    }
    else
    {
        newptr = malloc(1*sizeof(struct yyfilebuffer));
        newptr->prev = top;
        newptr->filename = filename;
        newptr->f = fopen(filename,"r");
        newptr->bs = yy_create_buffer(newptr->f, YY_BUF_SIZE);
        top = newptr;
        yy_switch_to_buffer(top->bs);   //edw
    }
    if(top->prev != NULL)
    {
        printf("top->prev points to file : %s\n",top->prev->filename);
    }
}

void popfile()
{
    struct yyfilebuffer *temp;      
    temp = NULL;
    if(top->prev == NULL)
    {
        printf("\n Error : Trying to pop from empty stack");
        exit(1);
    }
    else
    {
        temp = top;
        top = temp->prev;
        yy_switch_to_buffer(top->bs);
        system("pause");
    }
}

1 个答案:

答案 0 :(得分:2)

您需要考虑如何管理内存,记住C实际上没有像您可能习惯使用其他语言那样的字符串类型。

您定义了一个全局变量:

char temporal[7];

(它有一个奇怪的名字,因为全局变量不是临时的),然后在你的词法分析器中填入它的值:

for(i=1;i<strlen(yytext)-2;i++) {
        temporal[i-1]=yytext[i];
}

上述代码至少有三个问题:

  1. temporal只有六个字符的文件名空间,但是你无处检查以确保yyleng不大于6.如果是,你将覆盖随机内存。 (flex生成的扫描程序将yyleng设置为起始地址为yytext的令牌的长度。因此,您也可以使用该值而不是计算strlen(yytext),这涉及扫描文本。)

  2. 您永远不会终止temporal。这是第一次,因为它具有静态生命周期,因此在程序初始化时将填充零。但是第二次和以后的时间你指望新的文件名不要比前一个短;否则,你最终会在新名称的末尾加上以前名字的一部分。

  3. 您可以更好地使用标准C库。虽然由于下面我将注意到的原因,但这并没有解决你观察到的问题,最好使用以下代替循环,检查yyleng不是太大:

    memcpy(temporal, yytext + 1, yyleng - 2); /* Copy the filename */
    temporal[yyleng - 2] = '\0';              /* NUL-terminate the copy */
    
  4. temporal中复制后,您可以将其提交给newfile

    newfile(temporal);
    

    newfile中,我们看到的是:

    newptr->filename = filename;
    

    不会复制文件名。对newfile的调用将temporal的地址作为参数传递,因此在newfile中,参数filename的值是temporal的地址。然后,您将该地址存储在newptr->filename中,因此newptr->filename也是temporal的地址。

    但是,如上所述,temporal不是暂时的。它是一个全局变量,其生命周期是程序的整个生命周期。因此,下次您的词汇扫描程序遇到include指令时,它会将其放入temporal,覆盖以前的内容。那么filename结构中的yyfilebuffer成员会发生什么呢?答:没事。它仍然指向同一个地方temporal,但该地方的内容已经改变。因此,当您稍后打印出filename字段指向的字符串的内容时,您将获得与第一次创建{{1}时恰好位于temporal的字符串不同的字符串。结构。

    总的来说,如果yyfilebuffernewfile“拥有”filebuffer堆栈中的内存,你会发现管理内存更容易。这意味着popfile应该将其参数的副本复制到新分配的存储中,newfile应该释放该存储,因为不再需要它。如果popfile复制,则调用newfile的词法扫描程序操作不需要复制;它只需要在调用newfile时确保字符串正确地以NUL方式终止。

    简而言之,代码可能如下所示:

    newfile

    既然/* Changed parameter to const, since we are not modifying its contents */ void newfile(const char *filename) { /* Eliminated this check as obviously unnecessary: if(top == NULL) */ struct yyfilebuffer *newptr = malloc(sizeof(struct yyfilebuffer)); newptr->prev = top; // Here we copy filename. Since I suspect that you are on Windows, // I'll write it out in full. Normally, I'd use strdup. newptr->filename = malloc(strlen(filename) + 1); strcpy(newptr->filename, filename); newptr->f = fopen(filename,"r"); newptr->bs = yy_create_buffer(newptr->f, YY_BUF_SIZE); top = newptr; yy_switch_to_buffer(top->bs); //edw if(top->prev != NULL) { printf("top->prev points to file : %s\n",top->prev->filename); } } void popfile() { if(top->prev == NULL) { fprintf(stderr, "Error : Trying to pop from empty stack\n"); exit(1); } struct yyfilebuffer temp = top; top = temp->prev; /* Reclaim memory */ free(temp->filename); free(temp); yy_switch_to_buffer(top->bs); system("pause"); } 取得了传递给它的字符串的所有权,我们就不再需要复制了。由于该操作清楚地表明您希望newfile的参数类似于C #include指令(由#include"..."包围),因此最好明确指出:

    <...>