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

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分支:

  1. 解析到:时,代码初始化新Pair并赋值给json->pairs[json->pairsSize],但此时pairsSize未递增。
  2. 因为没有break,代码立刻进入NEXT_PAIR分支,处理当前Pair的value后执行pairsSize++。
  3. 当解析到下一个,时,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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 02:17:35