0

次の 2 つの関数を検討してください。

void foo(char * __restrict localPtr)
{
    // some work with localPtr
}

void bar(char * __restrict ptr)
{
    // some work with ptr
    foo(_ptr);
    // some other work with ptr
}

ptrで宣言されている__restrictように、bar呼び出しはfoo()危険ですか? 危険とは、 が指すメモリ ゾーンが のメモリ ゾーンとlocalPtr重複することを意味しptrます。それについてのガイドラインは何ですか?

4

1 に答える 1

3

restrict修飾子は、呼び出された関数が渡されたメモリにアクセスする唯一の方法がlocalPtrそのポインターfooを介することを意味します。そのメモリにはエイリアスはありません。これにより、別のポインターもデータを変更することを心配する必要がないため、オプティマイザーはより良いコードを生成できる可能性があります。

このコンテキスト、および他のほとんどのコンテキストでは、restrict修飾子は、呼び出しを行っているプログラマーに責任を負わせ、「エイリアスなし」の要件を満たしていることを確認します。

上記のコードから目に見える危険はありません。

を使用する場合、ほとんどの場合、引数リストに複数のポインターがあることに注意してくださいrestrict。C 標準から、以下を比較します。

void *memcpy(void *restrict s1, const void *restrict s2, size_t n);
void *memmove(void *s1, const void *s2, size_t n);

最初の ( memcpy()) は、メモリのチャンクと[s1 .. s1+n-1]オーバー[s2 .. s2+n-1]ラップしてはならないことを示しています。実際にオーバーラップすると、未定義の動作が発生します。2 番目の ( memmove()) はその要件を課していません。


foo()しかし、データを変更している別のポインターを作成するための呼び出しではありませんか?

はい、いいえ、どちらかと言えば...しかし、ほとんどはいいえです。

のポインタfoo()は確かに に渡されますbar()が、が実行されている間、メモリにアクセスできるbar()唯一の方法は、渡されたポインタを使用することです。bar()したがってbar()、動作しているメモリのエイリアスがないという前提でコンパイルできます。

コンパイラが を処理しているとき、引数が評価された後、関数が呼び出される前、および関数が戻るときに別foo()のシーケンス ポイントがあることを認識 (確認) します。が渡されなかったためbar()、データが によって変更された可能性があることを認識しています。したがって、これらの可能性を考慮したコードが生成されます。ただし、コンパイラは、によってアドレス指定されたメモリにアクセスできる唯一の方法は経由であることも認識しており(それが言うことです)、その仮定に基づいて処理を進めることができます。bar()const char *foo()localPtrlocalPtrrestrict

したがって、 がbar()呼び出されている間にポインタの 2 番目のコピーが存在しますが、これは の規則に違反していませんrestrict

于 2012-10-06T15:16:43.980 に答える