为什么 vector.erase() 运行 进入SIGABRT?

Why does vector.erase() run into SIGABRT?

我的文件路径规范化函数有一个奇怪的问题,我没有完全理解并且无法修复(我在 C++ 方面也不是很熟练)。

/**
 * Converts any path (e.g. /a/d/../b/.//c/) to absolute /a/b/c format.
 * @param path Any valid path beginning with /
 * @return Path in absolute /a/b/c format.
 */
static std::string normalizePath(std::string path)
{
    if (path == "/")
        return "/";

    if (path[0] != '/') // full relative paths not supported due to lack of context
        return "";

    std::vector<std::string> segments = strsplit(path, '/');
    while (segments[0] == "." || segments[0] == "..")
        segments.erase(segments.begin());

    for (int i = 0; i < segments.size(); i++)
    {
        if (segments[i] == "." || segments[i].empty())
            segments.erase(segments.begin() + (i--));
        else if (segments[i] == "..")
            segments.erase(segments.begin() + (--i), segments.begin() + (i+2)); // SIGABRT
    }

    std::string r;
    for (int i = 0; i < segments.size(); i++)
        r += "/" + segments[i];

    return r;
}

它适用于大多数输入,但输入 "/a/.."(应该是 return "/")使它在指定行与 SIGABRT 一起崩溃。

我的理解是我正在删除当前和上一个元素,但显然这个假设是错误的。

我也不愿意只使用 realpath() 因为我正在使用虚拟路径并且我绝对不希望对任何文件系统进行任何调用。

为什么我的代码会崩溃? 我如何让它按预期工作?

此行具有未定义的行为,因为它在访问彼此无序的上下文中访问了 i 两次:

segments.erase(segments.begin() + (--i), segments.begin() + (i+2));

由于未指定求值顺序,并且应用副作用的顺序未知,segments.begin() + (i+2) 可以求值到向量末尾的迭代器。

您可以通过使用 i 的值而不进行预减,并在从 erase 返回后应用 -- 来解决此问题:

else if (segments[i] == "..") {
    segments.erase(std::next(segments.begin(), i-1), std::next(segments.begin(), i+1));
    --i;
}

注意:上面的代码使用了std::next而不是给迭代器加数字