【问题标题】:strtok_r results in segmentation faultstrtok_r 导致分段错误
【发布时间】:2018-07-20 08:34:17
【问题描述】:

此代码用于解析 csv 文件,但会导致分段错误。 我可以在这里看到类似的代码:Nested strtok function problem in C

它们看起来相同,但我的代码导致分段错误。为什么?

#include <cstdlib>
#include <string.h>
#include <stdio.h>
#include <vector>

using namespace std;

struct inputTuple{
    int user, item, rating;
};

int main(void)
{
    char xdata[] = "1,88,0,874965758;1,2,1,876893171;1,99,1,878542960;";
    vector<inputTuple> input;

            int maxUser = 0, maxItem = 0;
        int user, item, rating;

        char *end_str;
        char *data_point = strtok_r(xdata, ";", &end_str);

        while(data_point != NULL) {
            char *end_attr;
            char *data_point_attr = strtok_r(data_point, ",", &end_attr);
            while(data_point_attr != NULL) {

                user = atoi(data_point_attr);
                data_point_attr = strtok_r(NULL, ",", &end_attr);
                item = atoi(data_point_attr);
                data_point_attr = strtok_r(NULL, ",", &end_attr);
                rating = atoi(data_point_attr);
                strtok_r(NULL, ",", &end_attr);
                input.push_back({user, item, rating});
                maxUser = max(maxUser, user);
                maxItem = max(maxItem, item);
                }
            data_point = strtok_r(NULL, ";", &end_str);
        }
    return 0;
}

【问题讨论】:

  • 不要在 c++ 中使用strtok()。也不一定是strtok() 的错。
  • 你试过调试吗?
  • 我有使用它的限制。在英特尔 SGX 飞地下。
  • 如果一行没有所有字段data_point_attr = strtok_r(NULL, ",", &amp;end_attr);返回NULLatoi触发段错误,可能是最后一个token的情况
  • 不,即使我删除也不行;从最后。 coliru.stacked-crooked.com/a/dee251b2308b7cb9

标签: c++ segmentation-fault


【解决方案1】:

让-弗朗索瓦是正确的。只需在调用atoi 之前检查值,如下所示。每次拨打atoi时都需要这样做

if(data_point_attr != NULL)
    user = atoi(data_point_attr);

【讨论】:

  • 好的,但内部循环是问题所在。防止 NULL 会在 vector 中创建过多的条目
  • @Jean-FrançoisFabre,是的,你是对的。我投了你的票:-)
【解决方案2】:

您的内部循环不需要并且对您的程序有害。

内循环提取3个token,然后继续,提取第4个token但在中间到达NULLatoi通过NULL:segfault。

您只需要一个循环(尽管添加了assert 语句以保持理智):

    while(data_point != NULL) {
        char *end_attr;
        char *data_point_attr = strtok_r(data_point, ",", &end_attr);

            user = atoi(data_point_attr);
            data_point_attr = strtok_r(NULL, ",", &end_attr);
            assert(data_point_attr != NULL);
            item = atoi(data_point_attr);
            data_point_attr = strtok_r(NULL, ",", &end_attr);
            assert(data_point_attr != NULL);
            rating = atoi(data_point_attr);
            strtok_r(NULL, ",", &end_attr);
            input.push_back({user, item, rating});
            maxUser = max(maxUser, user);
            maxItem = max(maxItem, item);

        data_point = strtok_r(NULL, ";", &end_str);
    }

更好的解决方案是使用真正的 C++ 解决方案来拆分字符串。

【讨论】:

    【解决方案3】:

    根据 Valgrind 的说法,您的程序在这里失败了:

                    item = atoi(data_point_attr);
    

    ,因为函数参数为 NULL。这似乎与让-弗朗索瓦的预测一致。使用 gdb 进行的一些调试表明它发生在第二次循环迭代中,尽管您甚至可以通过打印语句调试确定这一点。使用 gdb 进行更多调试即可显示发生了什么:您的内部循环有问题,因为当您到达记录末尾时,循环条件不会变为假。

    无论如何,你需要那个内部循环做什么?没有。您正在通过单独的语句读取每个值,因此您已经有效地展开了循环vs. 在程序中使用 arbitrary CSV 所需的内容。 检查每个strtok_r() 调用的结果,但删除不需要的内部循环。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-01-14
      • 1970-01-01
      • 2011-11-06
      • 2020-12-31
      • 2019-07-21
      • 2018-07-28
      • 2014-04-25
      相关资源
      最近更新 更多