C语言JSON解释器自增pairsSize触发Segmentation Fault问题
C语言JSON解释器段错误问题
我正在用C语言开发JSON解释器,现在碰到一个问题:每次添加对象值时,调用json->pairsSize++就会触发段错误(Segmentation Fault)。排查后发现,case NEXT_PAIR分支里对json->pairs[json->pairsSize]->values调用realloc或malloc时会出错,但注释掉json->pairsSize++后,代码虽然会覆盖已有数据,但能正常运行。
相关代码文件
json.c
#include <stdio.h> #include <stdlib.h> #include <limits.h> #include <string.h> #include <stdbool.h> #include <json.h> /* Local Data */ void remove_spacing(char * contents); /* Init/Cleanup */ struct JSON * init_json() { struct JSON * json = malloc(sizeof(struct JSON)+1); json->objectName = malloc(1); json->objectName[0] = '\0'; json->pairs = malloc(sizeof(struct Pair *)); json->pairsSize = 0; json->objects = malloc(sizeof(struct JSON *)); json->objectsSize = 0; json->prev = NULL; return json; } struct Pair * init_pair() { struct Pair * pair = malloc(sizeof(struct Pair)); pair->key = malloc(1); pair->key[0] = '\0'; pair->values = malloc(sizeof(void*)); pair->valuesSize = 0; return pair; } void interpret(struct JSON * json, char * filePath) { FILE * json_file; json_file = fopen(filePath, "r"); if (!json_file) { printf("Couldn't find JSON file %s\n", filePath); exit(EXIT_FAILURE); } char * jsonContent = malloc(1); unsigned int jsonContentPtr = 0; char ch; while(!feof(json_file)) { ch = fgetc(json_file); jsonContent = realloc(jsonContent, jsonContentPtr+1); jsonContent[jsonContentPtr] = ch; jsonContentPtr++; } jsonContent[jsonContentPtr] = '\0'; printf("%s\n", jsonContent); // Now the fun begins... size_t jsonContentLen = strlen(jsonContent); char * placeHolder = malloc(1); unsigned int placeHolderIndex = 0; bool isArray = false; for (size_t i = 0; i < jsonContentLen; i++) { switch(jsonContent[i]) { case OBJECT_OPEN: { if (i != 0 && isArray == false) { struct JSON * newJson = init_json(); add_object(json, newJson); json = json->objects[json->objectsSize-1]; // // Set objectName placeHolder[placeHolderIndex] = '\0'; set_objectName(json, placeHolder); placeHolder[0] = '\0'; placeHolderIndex = 0; } else if (isArray == true) { } break; } case OBJECT_CLOSE: { // Add last value if (json->prev != NULL) { json = json->prev; } break; } case KEYPAIR_DELIM: { placeHolder[placeHolderIndex] = '\0'; json->pairs = realloc(json->pairs, sizeof(struct Pair *) * (json->pairsSize+1)); json->pairs[json->pairsSize] = init_pair(); json->pairs[json->pairsSize]->key = realloc(json->pairs[json->pairsSize]->key, placeHolderIndex+1); strncpy(json->pairs[json->pairsSize]->key, placeHolder, placeHolderIndex+1); printf("%s\n", json->pairs[json->pairsSize]->key); placeHolder[0] = '\0'; placeHolderIndex = 0; } case NEXT_PAIR: { placeHolder[placeHolderIndex] = '\0'; size_t placeHolderLen = strlen(placeHolder); // Where the error occurs everytime i have json->pairsSize++ uncommented json->pairs[json->pairsSize]->values = realloc(json->pairs[json->pairsSize]->values, sizeof(void *) * (json->pairs[json->pairsSize]->valuesSize+1)); json->pairs[json->pairsSize]->values[json->pairs[json->pairsSize]->valuesSize] = malloc(placeHolderLen+1); strncpy(json->pairs[json->pairsSize]->values[json->pairs[json->pairsSize]->valuesSize], placeHolder, placeHolderLen+1); printf("%s\n", json->pairs[json->pairsSize]->values[json->pairs[json->pairsSize]->valuesSize]); placeHolder[0] = '\0'; placeHolderIndex = 0; json->pairs[json->pairsSize]->valuesSize++; if (isArray == false) { // Causes a segmentation fault for some reason, can't figure it out json->pairsSize++; } break; } case DOUBLE_QUOTE: { break; } default: { placeHolder = realloc(placeHolder, placeHolderIndex+1); placeHolder[placeHolderIndex] = jsonContent[i]; placeHolderIndex++; break; } } } free(placeHolder); fclose(json_file); } /* Setters */ void set_objectName(struct JSON * json, char * value) { size_t valueLen = strlen(value); json->objectName = realloc(json->objectName, valueLen+1); strcpy(json->objectName, value); } void add_object(struct JSON * json, struct JSON * newJSON) { newJSON->prev = json; json->objects = realloc(json->objects, sizeof(struct JSON *)*(json->objectsSize+1)); json->objects[json->objectsSize] = newJSON; json->objectsSize++; }
json.h
#ifndef C_JSON_H #define C_JSON_H #define DOUBLE_QUOTE '"' #define KEYPAIR_DELIM ':' #define OBJECT_OPEN '{' #define OBJECT_CLOSE '}' #define ARRAY_OPEN '[' #define ARRAY_CLOSE ']' #define NEXT_PAIR ',' struct Pair { char * key; void ** values; unsigned int valuesSize; }; struct JSON { char * objectName; struct Pair ** pairs; unsigned int pairsSize; struct JSON ** objects; unsigned int objectsSize; struct JSON * prev; }; struct JSON * init_json(); struct Pair * init_pair(); void interpret(struct JSON * json, char * filePath); /* Setters */ void set_objectName(struct JSON * json, char * value); #endif
main.c
#include <json.h> #include <stdio.h> int main(void) { struct JSON * json = init_json(); interpret(json, "./tests/objects.json"); printf("\n%s\n", json->objects[0]->pairs[0]->key); return 0; }
测试文件 objects.json
{ "Test-Object-1": { "Song": "My Heroine", "Author": "Silverstein", "Length": 240 } }
错误原因分析
核心问题出在**case KEYPAIR_DELIM分支没有加break语句**,导致代码执行完该分支后,直接进入case NEXT_PAIR分支:
- 解析到
:时,代码初始化新Pair并赋值给json->pairs[json->pairsSize],但此时pairsSize未递增。 - 因为没有
break,代码立刻进入NEXT_PAIR分支,处理当前Pair的value后执行pairsSize++。 - 当解析到下一个
,时,pairsSize已经指向未初始化的内存区域(新Pair还没在KEYPAIR_DELIM分支创建),此时访问json->pairs[json->pairsSize]->values直接触发段错误。
另外还有几个次要问题:
init_json中malloc(sizeof(struct JSON)+1)多分配1字节无意义,结构体大小已足够。- 文件读取用
while(!feof(json_file))会多读一个EOF字符。 strncpy的使用不够严谨,虽然当前场景暂时没问题,但strcpy更符合需求。
修复方案
1. 修复分支跳转问题
在case KEYPAIR_DELIM分支末尾添加break;,并调整pairsSize的递增时机:
case KEYPAIR_DELIM: { placeHolder[placeHolderIndex] = '\0'; json->pairs = realloc(json->pairs, sizeof(struct Pair *) * (json->pairsSize+1)); json->pairs[json->pairsSize] = init_pair(); json->pairs[json->pairsSize]->key = realloc(json->pairs[json->pairsSize]->key, placeHolderIndex+1); strcpy(json->pairs[json->pairsSize]->key, placeHolder); printf("%s\n", json->pairs[json->pairsSize]->key); placeHolder[0] = '\0'; placeHolderIndex = 0; // Pair初始化完成后立刻递增pairsSize json->pairsSize++; break; // 添加break避免进入NEXT_PAIR分支 }
2. 修改NEXT_PAIR分支的访问逻辑
因为pairsSize已经在KEYPAIR_DELIM分支递增,所以需要访问pairsSize-1来获取当前Pair:
case NEXT_PAIR: { placeHolder[placeHolderIndex] = '\0'; size_t placeHolderLen = strlen(placeHolder); // 访问最后一个已初始化的Pair json->pairs[json->pairsSize-1]->values = realloc(json->pairs[json->pairsSize-1]->values, sizeof(void *) * (json->pairs[json->pairsSize-1]->valuesSize+1)); json->pairs[json->pairsSize-1]->values[json->pairs[json->pairsSize-1]->valuesSize] = malloc(placeHolderLen+1); strcpy(json->pairs[json->pairsSize-1]->values[json->pairs[json->pairsSize-1]->valuesSize], placeHolder); printf("%s\n", json->pairs[json->pairsSize-1]->values[json->pairs[json->pairsSize-1]->valuesSize]); placeHolder[0] = '\0'; placeHolderIndex = 0; json->pairs[json->pairsSize-1]->valuesSize++; break; }
3. 修复文件读取逻辑
避免多读EOF字符:
char * jsonContent = malloc(1); unsigned int jsonContentPtr = 0; char ch; while((ch = fgetc(json_file)) != EOF) { jsonContent = realloc(jsonContent, jsonContentPtr+1); jsonContent[jsonContentPtr] = ch; jsonContentPtr++; } jsonContent[jsonContentPtr] = '\0';
4. 优化init_json函数
去掉多余的内存分配:
struct JSON * init_json() { struct JSON * json = malloc(sizeof(struct JSON)); json->objectName = malloc(1); json->objectName[0] = '\0'; json->pairs = malloc(sizeof(struct Pair *)); json->pairsSize = 0; json->objects = malloc(sizeof(struct JSON *)); json->objectsSize = 0; json->prev = NULL; return json; }
内容的提问来源于stack exchange,提问作者ThatPrimitive_Remastered
相关产品推荐
相关产品推荐

