1

私はCプログラミング言語でプロジェクトの一部としてリストに取り組んできました。参考までに、エントリとリストの構造は非常に基本的なデータ構造であり、そのように定義されています

typedef struct entry entry_t;
typedef struct list list_t;

struct entry
{
    void * data;
    entry_t * next;
};

struct list
{
    size_t size;
    entry_t * head;
    entry_t * tail;
};

headそれぞれ最初と最後のエントリをtail指します。ヘッダーなどのオーバーヘッドエントリはありませんが、リストにはすべてのデータエントリがあるため、リストにエントリが1つしかない場合は、同じエントリを指す必要がありますheadtailその上、リスト内のすべてのエントリを削除するようなスニペットがあります

list_t list;
entry_t * current, * next;
for(
    current=list->head,
    next=current->next,
    free(current);
    current!=list->tail;
    current=next,
    next=current->next,
    free(current)
);

current問題は、メモリアドレスとlist->tailpoint toの値の比較が、currentポインタが解放された後に評価されることです。同じメモリブロックcurrentlist->tail指してcurrent解放されたとすると、評価の結果はどうなりますか?さらに重要なことに、結果は(それが何であれ)あなたの経験のすべての異なるコンパイラで決定論的ですか?私の場合、プログラムはMSVCでコンパイルされ、正しく実行されます。つまり、解放された後のメモリアドレスcurrentlist->tailポイントの値は等しくなります。current

4

4 に答える 4

2

この時点で重要なことではありませんが、リストをクリアする場合は、明らかなことを許してください。ただし、テール(またはサイズ)が何であるかは誰が気にしますか。とにかく無効になりそうです。なぜ単純ではないのですか?

// assuming list is a valid
list_t* list;

while (list->head)
{
    entry_t *tmp = list->head;
    list->head = list->head->next;
    free(tmp);
}
list->head = list->tail = NULL;
list->size = 0;
于 2012-11-20T12:54:18.513 に答える
1

技術的には、あなたはルールを破っています。6.2.4までに(2)

オブジェクトの存続期間は、ストレージがそのオブジェクト用に予約されることが保証されているプログラム実行の部分です。オブジェクトは存在し、定数アドレスを持ち、その存続期間を通じて最後に保存された値を保持します。オブジェクトがその存続期間外に参照された場合、動作は定義されていません。ポインタが指す(またはちょうど過ぎた)オブジェクトがその存続期間の終わりに達すると、ポインタの値は不確定になります。

C2011標準のn1570ドラフト(C99標準の同じ場所でほぼ同じ)の場合、その値は不確定になります(そして、それを実行freeする前と同じオブジェクトを指している場合、の値も不確定になります)。したがって、比較でそれを使用すると、未定義の動作が呼び出されます。currentcurrentlist->tailfreelist->tail

ポインタの有効性を追跡し、比較などごとにチェックする架空の実装では、クラッシュしたり、他の面白いことをしたりする可能性があります。

ただし、実際には、これを実装することは期待していません。そのため、ほぼ確実に期待どおりの結果が得られます。

WhozCraig が指摘したように、実際にはその追跡を行うプラットフォームがあります。ただし、これはすべての無効なポインターをとして扱うNULLため、正しく理解していれば、問題のコードでも期待どおりの結果が得られます。

于 2012-11-20T13:02:59.477 に答える
0

評価の結果は何ですか

C規格では、結果は未定義であるとされています。

そしてもっと重要なのは、あなたの経験におけるすべての異なるコンパイラーでの結果(それが何であれ)が決定論的であるということです

プロセスによって割り当てられたメモリは、通常、プロセスが終了するまでOSに戻されません。私が知っているすべてのコンパイラはcurrent!=list->tail、例外をスローせずにfalseと評価されます。

于 2012-11-20T12:59:06.053 に答える
0

これがfor loop実装です

entry_t *current,*prev,*next;  

  for(
        current = list->head;

        current != NULL;

        prev=current,
        current = current->next,
        free(prev),
        prev=NULL
    );
于 2012-11-20T13:03:26.937 に答える