2017-09-04 5 views
-1

私のファイルパスの正規化機能には奇妙な問題があります。私は完全に理解していないし、修正が難しいです(私はC++で味はあまりありません)。vector.erase()がSIGABRTを実行するのはなぜですか?

/** 
* 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; 
} 

それはほとんどの入力で正常に動作しますが、("/"を返すことになっている)入力"https://stackoverflow.com/a/.."は、それが指示されたラインでSIGABRTでクラッシュます。

私が理解しているのは、現在の要素と前の要素を削除することですが、その前提が間違っているようです。

私はバーチャルパスで作業しているので、realpath()を使用するのも嫌です。ファイルシステムへの呼び出しは絶対にしたくありません。

なぜコードがクラッシュするのですか? 意図したとおりに動作させるにはどうすればよいですか?それはアクセスが互いに対してunsequencedれる文脈で二回iにアクセスするため

+2

https://en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom – user0042

+1

同じ文で '--i'と' i + 2'を使用すると問題が発生しています。 「i + 2」が範囲外であり、関数パラメータの評価順序が不明であることは言うまでもない。 –

+1

@ user0042 std :: removeは特定の値を持つ要素を削除するためのようですが、特定のインデックスを持つ要素を削除したいと考えています。 –

答えて

1

この行は、動作は未定義た:

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

評価の順序が指定されていないため、及び印加側の順エフェクトが不明な場合、segments.begin() + (i+2)は、イテレータの過去ベクトルの終わりまで評価できます。

あなたはeraseからの復帰後プリデクリメントすることなくiの値を用いて、及び--を適用することによって、この問題を解決することができ:

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

注:上記のコードはイテレータに番号を追加する代わりにstd::nextを使用

関連する問題