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持有指针时,对象不会被销毁。具体修改如下:
- 修改
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; }
- 调整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;
- 修改
free_aadstring函数,同时释放结构体本身
void free_aadstring(AADString *string) { free(string->content); free(string); // 释放结构体内存 }
- 调整
get_stringimage函数参数(无需双指针,直接接收堆指针)
函数逻辑无需修改,仅参数保持AADString *stringimage即可,因为现在传入的是堆分配的指针。
额外注意
后续销毁Token链表时,需要遍历每个Token,调用free_aadstring释放对应的AADString,避免内存泄漏。
内容的提问来源于stack exchange,提问作者aegle
相关产品推荐
相关产品推荐

