C语言实现的Tokenizer无输出,疑似未定义行为求助
程序无输出的核心原因是未初始化的指针触发内存访问错误,导致程序在输出前崩溃,同时代码还存在悬垂指针、标识符未识别等问题,具体分析和修复如下:
1. 未初始化的上下文指针导致野指针访问
compile_source中创建的Hx_File_Parsing_Context是局部变量,prev_token、curr_token、next_token三个指针默认是随机垃圾值。进入parse_file后,第一行就执行指针赋值,随后在循环中直接free(ctx->prev_token->value),这相当于访问随机内存地址的成员,直接触发未定义行为(通常是程序崩溃),后续的printf根本无法执行。
修复方案:
- 在
compile_source中初始化上下文指针为NULL:
Hx_File_Parsing_Context ctx; ctx.line = 1; ctx.prev_token = NULL; ctx.curr_token = NULL; ctx.next_token = NULL;
- 修改
parse_file的循环逻辑,避免初始的无效指针操作,且只在指针非空时释放内存:
int parse_file(Hx_File_Parsing_Context* ctx, Hx_Compiler_Metadata* meta) { ctx->next_token = get_next_token(ctx, meta); printf("\nAll Tokens in the file = \n"); while (ctx->next_token->type != TOK_EOF) { // 移动指针 ctx->prev_token = ctx->curr_token; ctx->curr_token = ctx->next_token; ctx->next_token = get_next_token(ctx, meta); // 打印当前Token printf("{ type: %i, value: %s, line: %i },\n", (int)ctx->curr_token->type, ctx->curr_token->value, ctx->curr_token->line); // 释放前一个Token(非空时) if (ctx->prev_token != NULL) { free(ctx->prev_token->value); free(ctx->prev_token); } } // 清理剩余Token if (ctx->next_token != NULL) { free(ctx->next_token->value); free(ctx->next_token); } if (ctx->curr_token != NULL) { free(ctx->curr_token->value); free(ctx->curr_token); } if (ctx->prev_token != NULL) { free(ctx->prev_token->value); free(ctx->prev_token); } return 0; }
2. Token的value存在悬垂指针问题
在get_next_token中,res->value = tok_val->data;后调用free_string(tok_val),而free_string会释放tok_val->data的内存,导致res->value指向已释放的内存,后续访问该值会触发未定义行为。
修复方案:
使用strdup复制字符串,将所有权转移给Token,避免悬垂指针:
// 替换原代码中res->value = tok_val->data; res->value = strdup(tok_val->data); free_string(tok_val);
3. 标识符未被正确识别
当前代码未处理字母/下划线开头的标识符(如测试文件中的xxy),会被错误归类为TOK_ILLEGAL。
修复方案:
在get_next_token中添加标识符处理分支,放在非法字符分支之前:
} else if (isalpha(ch) || ch == '_') { tok_val = append_char_to_string(tok_val, ch); tok_type = TOK_IDENTIFIER; do { ch = fgetc(ctx->fp); if (isalnum(ch) || ch == '_') { tok_val = append_char_to_string(tok_val, ch); } else { ungetc(ch, ctx->fp); break; } } while (isalnum(ch) || ch == '_'); } else { tok_val = append_char_to_string(tok_val, ch); tok_type = TOK_ILLEGAL; }
4. 枚举值重复问题
TOK_EXPO_LIT的枚举值和TOK_FLOAT_LIT均为7,会导致类型判断错误,修正枚举定义:
typedef enum { TOK_EOF, // End of file 0 TOK_ILLEGAL, // Illegal token 1 TOK_SPACE, // Space 2 TOK_VAR, // var 3 TOK_CONST, // const 4 TOK_ASSIGN, // = 5 TOK_INT_LIT, // Integer literal 6 TOK_FLOAT_LIT, // Float literal 7 TOK_EXPO_LIT, // Exponential literal 8 TOK_BOOL_LIT, // Bool literal 9 TOK_IDENTIFIER, // Identifier 10 } Hx_Token_Type;
5. 赋值符号未被识别
测试文件中的=会被归类为TOK_ILLEGAL,需要添加单独分支处理:
在get_next_token中,在空格分支之后、数字分支之前添加:
} else if (ch == '=') { tok_type = TOK_ASSIGN; append_char_to_string(tok_val, ch);
完成上述修复后,程序即可正常输出分词结果。
内容的提问来源于stack exchange,提问作者Rishav Sharan

