词法分析器正确输出但指针 "filename" 包含错误名称
lexical analyser correct output but pointer "filename" contains wrong name
此代码的目的是读取以下文本 (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");
}
}
您需要考虑如何管理内存,记住 C 并不像其他语言那样真正具有字符串类型。
你定义了一个全局变量:
char temporal[7];
(它有一个奇怪的名字,因为全局变量不是临时的),然后在你的词法分析器中填写它的值:
for(i=1;i<strlen(yytext)-2;i++) {
temporal[i-1]=yytext[i];
}
以上代码至少存在三个问题:
temporal
只能容纳六个字符的文件名,但您无处检查以确保 yyleng
不大于 6。如果是,您将覆盖随机内存。 (flex 生成的扫描器将 yyleng
设置为起始地址为 yytext
的令牌的长度。因此您不妨使用该值而不是计算 strlen(yytext)
,这涉及扫描正文。)
你永远不会以 null 终止 temporal
。第一次没问题,因为它有静态生命周期,因此会在程序初始化时用零填充。但是第二次和以后你指望新文件名不会比前一个短;否则,您最终会在新名称的末尾使用旧名称的一部分。
您本可以更好地利用标准 C 库。尽管出于我将在下面指出的原因,这并不能解决您观察到的问题,但最好使用以下而不是循环,在检查 之后 yyleng
是不太大:
memcpy(temporal, yytext + 1, yyleng - 2); /* Copy the filename */
temporal[yyleng - 2] = '[=12=]'; /* NUL-terminate the copy */
在 temporal
中制作副本后,将其交给 newfile
:
newfile(temporal);
而在newfile
中,我们看到的是:
newptr->filename = filename;
不复制文件名。对 newfile
的调用将 temporal
的地址作为参数传递,因此在 newfile
中,参数 filename
的值是 temporal
的地址。然后将该地址存储在 newptr->filename
中,因此 newptr->filename
也是 temporal
的地址。
但是,如上所述,temporal
不是暂时的。它是一个全局变量,其生命周期是程序的整个生命周期。所以下次你的词法扫描器遇到 include
指令时,它会将它放入 temporal
,覆盖之前的内容。那么 yyfilebuffer
结构中的 filename
成员会发生什么?答:没什么。它仍然指向同一个地方,temporal
,但是那个地方的内容已经改变了。因此,当您稍后打印出 filename
字段指向的字符串的内容时,您将得到一个与您第一次创建 [=39] 时碰巧在 temporal
中的字符串不同的字符串=]结构。
总的来说,如果newfile
和popfile
"own"文件缓冲区堆栈中的内存,您会发现管理内存更容易。这意味着 newfile
应该将其参数复制到新分配的存储中,并且 popfile
应该释放该存储,因为它不再需要了。如果 newfile
进行了复制,那么调用 newfile
的词法扫描器动作就没有必要进行复制;它只需要在调用 newfile
.
时确保字符串正确以 NUL 结尾
简而言之,代码可能如下所示:
/* 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
获得了传递给它的字符串的所有权,我们不再需要复制。由于该操作清楚地表明您希望 #include
的参数类似于 C #include
指令(被 "..."
或 <...>
包围),因此最好明确说明:
<INC>\".+\"$|"<".+">"$ {
/* NUL-terminate the filename by overwriting the trailing "*/
yytext[yyleng - 1] = '[=16=]';
newfile(yytext + 1);
BEGIN INITIAL;
}
此代码的目的是读取以下文本 (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");
}
}
您需要考虑如何管理内存,记住 C 并不像其他语言那样真正具有字符串类型。
你定义了一个全局变量:
char temporal[7];
(它有一个奇怪的名字,因为全局变量不是临时的),然后在你的词法分析器中填写它的值:
for(i=1;i<strlen(yytext)-2;i++) {
temporal[i-1]=yytext[i];
}
以上代码至少存在三个问题:
temporal
只能容纳六个字符的文件名,但您无处检查以确保yyleng
不大于 6。如果是,您将覆盖随机内存。 (flex 生成的扫描器将yyleng
设置为起始地址为yytext
的令牌的长度。因此您不妨使用该值而不是计算strlen(yytext)
,这涉及扫描正文。)你永远不会以 null 终止
temporal
。第一次没问题,因为它有静态生命周期,因此会在程序初始化时用零填充。但是第二次和以后你指望新文件名不会比前一个短;否则,您最终会在新名称的末尾使用旧名称的一部分。您本可以更好地利用标准 C 库。尽管出于我将在下面指出的原因,这并不能解决您观察到的问题,但最好使用以下而不是循环,在检查 之后
yyleng
是不太大:memcpy(temporal, yytext + 1, yyleng - 2); /* Copy the filename */ temporal[yyleng - 2] = '[=12=]'; /* NUL-terminate the copy */
在 temporal
中制作副本后,将其交给 newfile
:
newfile(temporal);
而在newfile
中,我们看到的是:
newptr->filename = filename;
不复制文件名。对 newfile
的调用将 temporal
的地址作为参数传递,因此在 newfile
中,参数 filename
的值是 temporal
的地址。然后将该地址存储在 newptr->filename
中,因此 newptr->filename
也是 temporal
的地址。
但是,如上所述,temporal
不是暂时的。它是一个全局变量,其生命周期是程序的整个生命周期。所以下次你的词法扫描器遇到 include
指令时,它会将它放入 temporal
,覆盖之前的内容。那么 yyfilebuffer
结构中的 filename
成员会发生什么?答:没什么。它仍然指向同一个地方,temporal
,但是那个地方的内容已经改变了。因此,当您稍后打印出 filename
字段指向的字符串的内容时,您将得到一个与您第一次创建 [=39] 时碰巧在 temporal
中的字符串不同的字符串=]结构。
总的来说,如果newfile
和popfile
"own"文件缓冲区堆栈中的内存,您会发现管理内存更容易。这意味着 newfile
应该将其参数复制到新分配的存储中,并且 popfile
应该释放该存储,因为它不再需要了。如果 newfile
进行了复制,那么调用 newfile
的词法扫描器动作就没有必要进行复制;它只需要在调用 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
获得了传递给它的字符串的所有权,我们不再需要复制。由于该操作清楚地表明您希望 #include
的参数类似于 C #include
指令(被 "..."
或 <...>
包围),因此最好明确说明:
<INC>\".+\"$|"<".+">"$ {
/* NUL-terminate the filename by overwriting the trailing "*/
yytext[yyleng - 1] = '[=16=]';
newfile(yytext + 1);
BEGIN INITIAL;
}