You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 04:42:03