【问题标题】:Why does this method throw a Segmentation fault?为什么此方法会引发分段错误?
【发布时间】:2013-08-07 17:09:33
【问题描述】:

我正在使用jsmn JSON parser (source code) 从 JSON 中获取一些文本。 jsmn 将数据存储在令牌中,但令牌不保存任何数据,它们只是指向 JSON 字符串中的令牌边界。例如,jsmn 将创建如下标记:

  • 对象 [0..31]
  • 字符串 [3..7]、字符串 [12..16]、字符串 [20..23]
  • 编号 [27..29]

此方法用于检索这些值之间的实际字符(对于字符串对象):

char* getTextFromJSON(const char *json)
{
    if (!json) return NULL;

    json_parser p;
    #define N_TOKENS 15  // this normally would be at the start of the file
    jsontok_t tokens[N_TOKENS];

    initJsonParser(&p);
    int err parseJson(&p, json, tokens, N_TOKENS);
    if (err) {
    fprintf(stdout, "Error parsing JSON: %d\n", err);
    return NULL;
    }
    for (int i = 0; i < N_TOKENS; ++i) {
        jsontok_t *key = &tokens[i];
        if (!memcmp("utterance", &json[key->start], (size_t) (key->end - key->start))) {
            ++key;
            return strndup(&json[key->start], (size_t)(key->end - key->start));
        }
    }
    return NULL;
}

以下是一些将被放入解析器的 JSON 示例:

  • {"status":0,"id":"432eac38858968c108899cc6c3a4bade-1","hypotheses":[{"utterance":"test","confidence":0.84134156}]}
  • {"status":5,"id":"695118aaa3d01dc2ac4aa8054d1e5bb0-1","hypotheses":[]}

将第一个示例 JSON 传递给该方法后,我得到了从该方法返回的“test”的预期值。但是,在将空 JSON 传递给该方法后,我在条件 if 语句的 for 循环的第 8 次迭代中遇到了分段错误。

有什么建议吗?

这里是十六进制值:

key->start: 0x00000000
key->end - key->start: 0x00000046
key->start: 0x00000002
key->end - key->start: 0x00000006
key->start: 0x0000000A
key->end - key->start: 0x00000001
key->start: 0x0000000D
key->end - key->start: 0x00000002
key->start: 0x00000012
key->end - key->start: 0x00000022
key->start: 0x00000037
key->end - key->start: 0x0000000A
key->start: 0x00000043
key->end - key->start: 0x00000002
key->start: 0x3A7B3188
key->end - key->start: 0x7A0F0766

【问题讨论】:

  • 你真的要把&amp;jason[..]传递给memcpy吗?
  • @UchiaItachi 在那个方法中没有memcpy...
  • 在循环中添加printf() 语句,以十六进制打印(key-&gt;start)(key-&gt;end - key-&gt;start) 的值(即:printf("0x%08X", val)。我倾向于您的输入字符串,@987654336 @,比预期的短,并且您传递的指针无效。
  • @Dogbert 将它们编辑到问题中。

标签: c json parsing segmentation-fault token


【解决方案1】:

EDIT查看源代码后...

for (i = parser->toknext; i < num_tokens; i++) {
    jsmn_fill_token(&tokens[i], JSMN_PRIMITIVE, -1, -1);
}

它初始化所有结构,但 ->start 和->end 将等于 -1,这就是 memcmp 失败的原因。

for (int i = 0; i < N_TOKENS; ++i) {
    jsontok_t *key = &tokens[i];
    if (key->start == -1) return NULL;
    if (!memcmp("utterance", &json[key->start], (size_t) (key->end - key->start))) {
        ++key;
        return strndup(&json[key->start], (size_t)(key->end - key->start));
    }
}

在 ->start 或 ->end 中检查 -1 值就足够了。

【讨论】:

  • 仍然出现分段错误:/
  • @syb0rg - 您永远不会检查 parseJson(...) 的结果,只有在该函数返回 JSMN_SUCCESS 时才应该继续。
  • 我更新了引入测试条件的方法。它仍然给出分段错误。
  • +1 感谢您提供的帮助。我已经介绍了您建议的测试用例,我的代码现在可以正常工作了。另一个答案只是让我更接近完全依赖的代码。感谢您的帮助!
【解决方案2】:

您的tokens[] 数组在您将其传递给parseJson() 之前未初始化,因此一旦您迭代超过最后一个令牌(第二个示例中的第七个),您将尝试在未初始化的无意义地址值上运行memcmp() .这导致你的段错误。将tokens[] 初始化为某个值,然后在for() 循环期间检查开始/结束字段中的初始化值。

例如,我可能会将tokens[] 初始化为零(通过memset(&amp;tokens, 0, sizeof(tokens));)并在循环的每次迭代期间检查长度为零(key-&gt;end - key-&gt;start),以查看令牌在传递给之前是否实际有效memcmp()。如果令牌的长度为零,则使用 break; 退出循环。

(或者,如果令牌可以具有合法的零长度,请使用其他值。)

【讨论】:

  • 我会接受这个答案,因为它让我最接近解决方案。我按照你的建议做了,但我还为空白令牌添加了一个测试用例 (if (!memcmp("", &amp;json[key-&gt;start], (size_t) (key-&gt;end - key-&gt;start))) return NULL;)。现在代码没有给出分段错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多