第一弹 打印EOF

本文探讨了C语言中EOF(文件结束)的概念,解释了它在处理输入时的作用。当使用getchar函数且无输入或输入结束时,getchar返回的特殊值即为EOF。在VC++6.0环境下,EOF的值表现为10。通过单步调试,可以观察到在不同输入情况下getchar的行为,展示了如何识别和处理EOF。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

EOF是什么?

  • EOF(End Of File,文件结束)。
  • 在用getchar,计算机需要区分文件中有效数据和输入结束符的问题。C语言采用的解决办法是:在没有输入时,getchar返回一个特殊值,这个特殊值与任何实际字符都不同,称为EOF。EOF在头文件<stdio.h>中定义,是一个int整型数。具体值不同平台有异。
  • 我们在声明变量c的时候,必须让它大到足以存放getchar的任何返回值。所以把c声明为int型,而不是char型。

打印EOF:

#include <stdio.h>

main()
{
	int c;

	while(1)
	{
		c = getchar();
		putchar(c);
	}

}


上面函数的功能是,将输入的字符串输出。虽然getchar是读取单个字符串,但是用了while(1)循环,所以是字符串输入输出。在VC++6.0下F11单步调试:

①输入字符串的情况下,如输入helloworld

c为整型,104是helloworld里面第一个h的asc码。单步执行F10,putchar先打印出h

101是e的asc码,然后putchar打印出e,此时屏幕有“he”。以后依次类推。

直到最后一个d打印出来之后,在继续F10,会发现c为赋值为10:

#include <stdio.h> #include<string.h> #include <stdlib.h> // 解决atoi未声明问题 #define MAX_STATES 20 #define MAX_SYMBOLS 10 // 函数原型声明 int get_token(FILE *fp); // 显式声明词法分析器 int char_to_symbol(char c); // 补充字符转换函数声明 // 终结符和非终结符编码 enum Symbol {a, b, c, d, e, S, A, B, END}; // END表示$ // LR(0)分析表结构 typedef struct { char action[MAX_STATES][MAX_SYMBOLS][10]; // ACTION表 int goto_table[MAX_STATES][MAX_SYMBOLS]; // GOTO表 } LRTable; // 产生式结构 typedef struct { int lhs; // 左部符号 int rhs_len; // 右部长度 int rhs[5]; // 右部符号数组 } Production; // 初始化给定文法的分析表[^3][^4] void init_table(LRTable *t) { // ACTION表配置(示例数据需根据实际DFA构造) strcpy(t->action[0][a], "s1"); strcpy(t->action[1][b], "s3"); strcpy(t->action[2][A], "2"); // GOTO状态2 // ...其他状态配置 // GOTO表配置 t->goto_table[0][S] = 5; t->goto_table[1][A] = 6; // ...其他转移配置 } // 产生式配置(示例) Production productions[] = { {S, 5, {a, A, c, B, e}}, // S → aAcBe {A, 1, {b}}, // A → b {A, 2, {A, b}}, // A → Ab {B, 1, {d}} // B → d }; // LR分析核心函数 int parse(FILE *input, LRTable *table) { int state_stack[MAX_STATES] = {0}; int state_top = 0; int lookahead = get_token(input); // 读取第一个词元 while(1) { int curr_state = state_stack[state_top]; char *action = table->action[curr_state][lookahead]; if(!action) return 0; // 错误处理 if(strcmp(action, "acc") == 0) return 1; if(action[0] == 's') { // 移进动作 int new_state = atoi(action+1); state_stack[++state_top] = new_state; lookahead = get_token(input); } else if(action[0] == 'r') { // 规约动作 int prod_num = atoi(action+1); Production p = productions[prod_num]; // 出右部长度个状态 state_top -= p.rhs_len; if(state_top < 0) return 0; // GOTO转移 int new_state = table->goto_table[state_stack[state_top]][p.lhs]; state_stack[++state_top] = new_state; } } } // 将字符转换为符号编码 int char_to_symbol(char c) { switch(c) { case 'a': return a; case 'b': return b; // ...其他符号转换 default: return -1; } } // 读取词元流 int get_token(FILE *fp) { char ch; if(fscanf(fp, "%c", &ch) == EOF) return END; return char_to_symbol(ch); } int main() { FILE *input = fopen("input.txt", "r"); FILE *output = fopen("output.txt", "w"); LRTable table; init_table(&table); int result = parse(input, &table); fprintf(output, result ? "true" : "false"); fclose(input); fclose(output); return 0; } 为什么我运行的时候就卡住,运行不了呢
最新发布
05-14
<think>好的,用户现在的问题是C言中的LR分析器在运行时卡住,需要找出原因并提供解决方案。用户之前提到过编译错误,比如get_token和atoi未声明,现在可能解决了编译问题,但运行时出现了卡死的情况。 首先,我需要回顾用户的历史问题。之前用户遇到了编译错误,涉及get_token和atoi未声明。我建议添加头文件、函数声明等。现在运行时卡住,可能与之前的解决方案有关,也可能涉及其他逻辑错误。 根据用户提供的引用内容,特别是引用[1]提到Bison生成的yyparse函数的工作原理,每当需要下一个符号时调用yylex。用户的代码中可能有自定义的get_token函数代替了yylex,需要确认是否正确返回token,导致解析器无法继续。 可能的原因有几个方面: 1. **get_token函数实现问题**:如果get_token没有正确返回token或者遇到文件结束符,解析器可能一直等待输入。比如,返回的token值不在LR表中,导致解析器无法找到动作,进入死循环。 2. **LRTable结构问题**:状态表可能未正确初始化,例如action或goto表有错误,导致解析器在某个状态找不到对应的动作,陷入循环。 3. **atoi转换错误**:如果在解析action时,atoi转换失败,可能导致状态或产生式编号错误,进而引发无效状态转移。 4. **栈处理逻辑错误**:LR分析器的栈操作(压栈、栈)可能出错,导致状态无法正确推进,进入死循环。 5. **输入流问题**:输入文件可能未正确读取,或者token流提前结束,解析器等待更多输入而卡住。 需要逐步排查这些可能性。例如,检查get_token是否在文件结束时返回正确的END标记,LRTable中的action是否正确处理每个状态和输入符号的组合,atoi是否正确解析action字符串后的数字,确保栈操作正确,避免无限循环。 此外,用户引用[4]中的parse_memory函数处理ASS轨道,可能涉及内存管理问题,但用户的当前问题是否涉及内存泄漏或指针错误?比如,LRTable结构中的指针未正确初始化,导致访问越界,也可能引起程序卡死。 解决方案方面,可能需要添加调试输出,打印当前状态、符号和动作,观察程序卡在哪个步骤。检查栈的状态,确认是否出现重复的状态组合,导致无限循环。同时,验证LR表的正确性,确保每个状态和符号组合都有合法的动作,如移进、规约或接受。 另外,用户之前提到使用atoi,引用[2]和[5]中的字符串分割和转换,可能需要注意atoi的错误处理,例如遇到非数字字符时返回0,可能导致错误的状态编号。建议使用strtol替代atoi,以检测转换错误。 总结,需要从输入处理、状态表、栈操作、函数实现等多个方面进行排查,逐步验证每个部分的正确性,添加调试信息帮助定位问题点。</think>#### 一、问题原因分析 根据引用[1]描述的LR解析器工作原理,结合`get_token`和`LRTable`相关代码,运行时卡住的主要原因可能有: 1. **输入流未终止**:`get_token`未正确返回终止符号$END$(如引用[1]中yylex的EOF处理) 2. **状态表逻辑错误**:LRTable的action/goto表存在无效状态转换(如引用[4]解析轨道时的状态维护问题) 3. **栈溢出循环**:解析器在相同状态间无限循环(常见于错误的状态转移设计) 4. **符号转换错误**:`atoi`转换action数值失败(如引用[2]字符串分割转换的格式问题) #### 二、分步解决方案 ##### 1. 验证get_token终止逻辑 在词法分析器中添加EOF检测: ```c int get_token(FILE *fp) { static int counter = 0; if(counter++ > 100) { // 防止无限循环 fprintf(stderr, "Token overflow\n"); exit(1); } // ...原有逻辑... if(feof(fp)) return END; // 显式返回终止符[^1] } ``` ##### 2. 添加状态表调试输出 在解析循环中插入诊断信息: ```c printf("当前状态: %d, 输入符号: %d\n", current_state, lookahead); printf("Action表内容: %s\n", table->action[current_state][lookahead]); ``` ##### 3. 强化atoi容错处理 使用`strtol`替代`atoi`(引用[2]的最佳实践): ```c char *endptr; int new_state = strtol(action+1, &endptr, 10); if(*endptr != '\0') { fprintf(stderr, "无效状态转换指令: %s\n", action); exit(EXIT_FAILURE); } ``` ##### 4. 实现状态转移保护 增加状态循环检测: ```c #define MAX_STEPS 1000 int step_counter = 0; while(!stack_empty() && step_counter++ < MAX_STEPS) { // ...解析逻辑... } if(step_counter >= MAX_STEPS) { fprintf(stderr, "解析器在%d步内未终止\n", MAX_STEPS); } ``` ##### 5. 检查LRTable初始化 验证分析表数据结构(关键如引用[4]的轨道解析): ```c // 示例检查代码 for(int i=0; i<STATE_COUNT; i++) { for(int j=0; j<SYMBOL_COUNT; j++) { if(table->action[i][j] == NULL) { fprintf(stderr, "状态%d符号%d存在空action\n",i,j); } } } ``` #### 三、典型修复案例 ```c // 修正后的parse函数片段 int parse(FILE *input, LRTable *table) { int stack[100], top = -1; stack[++top] = 0; // 初始状态 int lookahead = get_token(input); while(1) { int state = stack[top]; char *action = table->action[state][lookahead]; if(!action) { fprintf(stderr, "在状态%d遇到无效符号%d\n", state, lookahead); return -1; } if(action[0] == 's') { // 移进动作 char *end; int new_state = strtol(action+1, &end, 10); if(*end != 0) { // 验证数字转换 fprintf(stderr, "非法移进指令: %s\n", action); return -1; } stack[++top] = new_state; lookahead = get_token(input); } else if(action[0] == 'r') { // 规约动作 Production *prod = &table->productions[atoi(action+1)]; top -= prod->rhs_len; // 出右部符号 int new_state = stack[top]; stack[++top] = table->goto_[new_state][prod->lhs]; // 压入新状态 } else if(strcmp(action, "acc") == 0) { return 0; // 接受 } else { fprintf(stderr, "未知动作类型: %s\n", action); return -1; } } } ``` #### 四、关联问题扩展 1. **如何设计LR分析表验证工具?** - 建议实现状态可达性分析和冲突检测算法[^1] 2. **如何处理二义性文法导致的死循环?** - 使用优先级和结合性声明消除冲突(参考Bison的处理方式)[^1] 3. **为什么推荐使用Flex/Bison组合?** - 自动生成健壮的词法/法分析器(如引用[1]的yyparse实现) --相关问题-- 1. 如何检测LR分析表中的移进-规约冲突? 2. C言中如何实现安全的栈结构? 3. 词法分析器与法分析器的交互协议设计要点? 4. 如何用GDB调试陷入死循环的解析器?
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值