10

この質問への回答で指摘されているように、コンパイラ (この場合は gcc-4.1.2、はい、古い、いいえ、変更できません) は、適切と思われる場所で構造体の割り当てを memcpy に置き換えることができます。

valgrind でいくつかのコードを実行していますが、memcpy のソース/宛先の重複に関する警告が表示されました。コードを見ると、次のように表示されます (言い換え):

struct outer
{
    struct inner i;
    // lots of other stuff
};

struct inner
{
    int x;
    // lots of other stuff
};

void frob(struct inner* i, struct outer* o)
{
    o->i = *i;
}

int main()
{
    struct outer o;

    // assign a bunch of fields in o->i...

    frob(&o.i, o);
    return 0;
}

gcc がその割り当てを に置き換えることを決定した場合memcpy、ソースと宛先が重複するため、無効な呼び出しになります。

明らかに、割り当てステートメントfrobを callに変更するとmemmove、問題はなくなります。

しかし、これはコンパイラのバグですか、それともその代入ステートメントは何らかの理由で無効ですか?

4

3 に答える 3

4

私の知る限り、これはコンパイラのバグです。タイプが一致し、コンパイラがのアドレスが以前に取得できなかったことを証明できないため、エイリアシングルールに従ってエイリアシングをi許可されます。そしてもちろん、重複する(または同じ)ポインターを使用して呼び出すと、UBが呼び出されます。&o.io.imemcpy

ちなみに、あなたの例でo->iは、ナンセンスであることに注意してください。あなたはo.i私が思うことを意味しました...

于 2011-03-23T22:06:36.330 に答える
4

レベルを混同していると思います。gcc正しい動作を保証できる限り、割り当て操作を好きなライブラリ関数の呼び出しに置き換えることは完全に正しいです。

memcpyそれは、標準的な意味での「呼び出し」などではありません。正確性を保証する追加情報を持っている可能性のあるライブラリの 1 つの関数を使用しているだけです。標準で説明されているのプロパティmemcpyは、コンパイラ/環境の実装者向けではなく、プログラマ向けのインターフェイスと見なされるプロパティです。

memcpy問題の実装で、代入操作を有効にする動作を実装するかどうかは別の問題です。それをチェックしたり、コードを調べたりすることはそれほど難しくありません。

于 2011-03-23T22:34:22.707 に答える
1

「0」ではなく「&o」というタイプミスがあると思います。この仮説では、「オーバーラップ」は実際には厳密な上書きです:memcpy(&o-> i、&o-> i、sizeof(o-> i))。この特定のケースでは、memcpyは正しく動作します。

于 2011-03-23T22:07:48.673 に答える