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

C语言JSON词法分析器STRING Token乱码问题求助

C语言JSON解析器词法分析器STRING Token乱码问题

自学实现C语言JSON解析器时,词法分析器(lexer)出现异常:除STRING Token外其余功能正常,但输出STRING Token时,内容显示为乱码,size也为无效值。

已完成以下排查:

  • 确认AADString的内存分配正常
  • 确认字符串从创建到存入Token的地址未变化
  • get_stringimage()执行后可正常打印AADString内容
  • new_token()内将字符串存入Token后也可正常打印

怀疑切换到下一个Token时出现问题,但无法定位具体原因。相关信息如下:


运行日志

Type: CURLYOPEN
Image: <NULL>
Line number: 1
|
V
Type: STRING
Image: ����UH��}��}�wQ�E�H�� of size 32765 at 0x7ffdfd6cea70
Line number: 2
|
V
Type: COLON
Image: <NULL>
Line number: 2
|
V
Type: NULL
Image: <NULL>
Line number: 2
|
V
Type: CURLYCLOSED
Image: <NULL>
Line number: 3
|
V
<NULL>

待分词的JSON

{
    "nullvalue" : null
}

相关代码片段

STRING Token生成代码

...
case '"':
            AADString stringimage = new_aadstring();
            if (!get_stringimage(fd, &stringimage)) return handle_lexerror(E_UNEXPECTEDTERMINATION_ERROR_LEXER_AJSON, linenr);
            new_token(&token, E_STRING_TOKEN_AJSON, &stringimage, linenr);
            break;
...

获取字符串内容的函数

int get_stringimage(FILE *fd, AADString *stringimage)
{
    char c;
    while ((c = fgetc(fd)) != '"')
    {
        if (c == EOF)
        {
            free_aadstring(stringimage);
            return 0;
        }
        if (c == '\\')
        {
            appendto_aadstring(c, stringimage);
            c = fgetc(fd);
            if (c == EOF)
            {
                free_aadstring(stringimage);
                return 0;
            }
        }
        appendto_aadstring(c, stringimage);
    }
    appendto_aadstring('\0', stringimage);
    return 1;
}

创建新Token的函数

void new_token(T_TokenAJSON **token, E_TypeTokenAJSON type, AADString *image, int linenr)
{
    (*token)->type = type;
    (*token)->image = image;
    (*token)->linenr = linenr;
    (*token)->next = (T_TokenAJSON *) malloc(sizeof(T_TokenAJSON));
    (*token)->next->next = NULL;
    (*token) = (*token)->next;
}

Token相关结构体与枚举

typedef enum 
{
    E_CURLYOPEN_TOKEN_AJSON,
    E_CURLYCLOSED_TOKEN_AJSON,
    E_SQUAREOPEN_TOKEN_AJSON,
    E_SQUARECLOSED_TOKEN_AJSON,
    E_COLON_TOKEN_AJSON,
    E_COMMA_TOKEN_AJSON,
    E_STRING_TOKEN_AJSON,
    E_NUMBER_TOKEN_AJSON,
    E_TRUE_TOKEN_AJSON,
    E_FALSE_TOKEN_AJSON,
    E_NULL_TOKEN_AJSON
} E_TypeTokenAJSON;

typedef struct T_TokenAJSON
{
    E_TypeTokenAJSON type;
    AADString *image;
    int linenr;
    struct T_TokenAJSON *next;
} T_TokenAJSON;

Token初始化代码

T_TokenAJSON *roottoken = (T_TokenAJSON *) malloc(sizeof(T_TokenAJSON));
roottoken->next = NULL;

AADString结构体及相关函数

typedef struct AADString
{
    int size;
    int nextidx;
    char *content;
} AADString;

AADString new_aadstring()
{
    AADString string;
    string.size = INIT_SIZE; // INIT_SIZE = 2
    string.nextidx = 0;
    string.content = (char *) malloc(INIT_SIZE*sizeof(char));
    return string;
}

void resize_aadstring(AADString *string)
{
    if (string->nextidx == string->size)
    {
        string->size *= 2;
        string->content = (char *) realloc(string->content, string->size*sizeof(char));
    }
}

void appendto_aadstring(char c, AADString *string)
{
    resize_aadstring(string);
    string->content[string->nextidx] = c;
    string->nextidx++;
}

void free_aadstring(AADString *string)
{
    free(string->content);
}

问题定位

核心原因是栈内存对象被销毁导致野指针:
在case '"'分支中,AADString stringimage是局部变量,存储在栈上。当分支执行完毕后,该变量的栈内存会被回收,但你把&stringimage传给了new_token,让Token的image指针指向这块栈内存。后续访问Token时,这块内存已经被覆盖,所以出现乱码和无效的size值。

解决方案

将AADString改为堆内存分配,确保Token持有指针时,对象不会被销毁。具体修改如下:

  1. 修改new_aadstring函数,返回堆分配的结构体指针
AADString* new_aadstring()
{
    AADString* string = malloc(sizeof(AADString));
    if (!string) {
        // 可添加内存分配失败处理
        return NULL;
    }
    string->size = INIT_SIZE; // INIT_SIZE = 2
    string->nextidx = 0;
    string->content = malloc(INIT_SIZE * sizeof(char));
    return string;
}
  1. 调整STRING Token生成代码,使用堆指针
case '"':
    AADString* stringimage = new_aadstring();
    if (!stringimage) {
        // 内存分配失败处理
        return handle_lexerror(E_MEMORY_ERROR_LEXER_AJSON, linenr);
    }
    if (!get_stringimage(fd, stringimage)) {
        handle_lexerror(E_UNEXPECTEDTERMINATION_ERROR_LEXER_AJSON, linenr);
        free_aadstring(stringimage);
        return;
    }
    new_token(&token, E_STRING_TOKEN_AJSON, stringimage, linenr);
    break;
  1. 修改free_aadstring函数,同时释放结构体本身
void free_aadstring(AADString *string)
{
    free(string->content);
    free(string); // 释放结构体内存
}
  1. 调整get_stringimage函数参数(无需双指针,直接接收堆指针)
    函数逻辑无需修改,仅参数保持AADString *stringimage即可,因为现在传入的是堆分配的指针。

额外注意

后续销毁Token链表时,需要遍历每个Token,调用free_aadstring释放对应的AADString,避免内存泄漏。


内容的提问来源于stack exchange,提问作者aegle

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 04:50:55