1

この例をとると:

std::map<int,foo*> intmap;
fillMap(intmap);

// I will force this to end(), in real life this could be a "find" output
std::map<int,foo*>::iterator iter = intmap.end(); 
if(iter->second != 0)
    iter->second->whatever();

「whatever()」呼び出しでセグメンテーション違反が発生しています(これは予想されますが、例では意図的に「iter!= intmap.end()」をチェックしていません)が、「->second」nullポインタチェックでは発生していません。それは期待される動作ですか?このセグメンテーション違反は「whatever()」呼び出しで体系的に発生しますか、それとも特定の実行時のメモリ条件に依存しますか?

コメントありがとうございます。ジャコモ

4

4 に答える 4

6

STL コンテナーの逆参照end()と過去の終了イテレーターは、未定義の動作です。expectedと呼べるものはありません。すべてが発生する可能性があり、機能することさえあります。コンパイラ/ライブラリ/OS のバージョン、ランタイム環境の状態、デバッグ/リリース ビルドなど、非常に多くの要因に依存する可能性があります。

于 2012-10-24T15:42:39.537 に答える
0

終わりを過ぎた反復子を逆参照した結果は、もちろん未定義の動作になるため、それが何をするかは保証されません。

ただし、何が起こる可能性があるかを検討することは有益です (シナリオのデバッグに役立ちます)。連想コンテナの典型的な実装は、ノードのバイナリ ツリーとして行われます。各ノードには、nextおよびpreviousノードへのポインタが反復順に含まれており、イテレータは、ノードへのポインタの薄いラッパーです。同様に、listは双方向リンク リストとして実装され、各ノードにはnextおよびpreviousノードへのポインタが含まれます。末尾反復子はデクリメント可能である必要があるため、最も単純な実装は、常に存在するノードをprevious指し、そのポインターがコンテナー内の最後のノードを指すことです。

この終わりのないノードは常に存在する必要があるため、空のコンテナーの場合でも、最も簡単な実装はコンテナー クラス自体の内部に配置することであり、ほとんどのライブラリ実装はこれを行います。その結果、そのストレージは自動 (関数ローカル) ストレージであり、デフォルトで構築されるため、末尾イテレーターを逆参照するとスタック ガベージが生成されます。

これは、ポインターを比較することで確認できます。

#include <map>
#include <iostream>
int main() {
    std::map<int, int> m;
    std::cout << &m << ' ' << &*m.end() << ' ' << &m + 1 << '\n';
}

0xbf990034 0xbf990048 0xbf99004c

ご覧のとおり、最後のノードのストレージはmapのスタック フットプリント内に含まれています。

于 2012-10-24T15:59:50.677 に答える
0

「終了」反復子を逆参照することはできません。iter->とほぼ同じであることを覚えておいてください(*iter).。つまり、逆参照があります。

于 2012-10-24T15:40:46.017 に答える
0

イテレータの逆参照end()は未定義の動作です。クラッシュする場合とクラッシュしない場合があります。

find次のような戻り値を確認する必要があります。

if(iter != intmap.end())
   iter->second->whatever();
于 2012-10-24T15:40:47.780 に答える