12

別の質問に答えている間、私は次の例を考えました:

void *p;
unsigned x = 17;

assert(sizeof(void*) >= sizeof(unsigned));
*(unsigned*)&p = 17;        // (1)
memcpy(&p, &x, sizeof(x));  // (2)

1行目はエイリアシングルールに違反しています。ただし、2行目は問題ありません。エイリアシングルール。問題は、なぜですか?コンパイラには、memcpyなどの関数に関する特別な組み込み知識がありますか、それともmemcpyをOKにする他のルールがありますか?エイリアシングルールを破ることなく、標準Cでmemcpyのような関数を実装する方法はありますか?

4

1 に答える 1

14

C標準はそれについて非常に明確です。によって指定されたオブジェクトの有効なタイプはpvoid*宣言されたタイプであるため、です。を参照してください6.5/6。C99のエイリアシング規則は読み取り書き込みに適用され、の左辺値をvoid*介した書き込みは、によると未定義の動作です。unsigned(1)6.5/7

対照的に、任意のオブジェクト()をエイリアスできるため、 memcpyof(2)は問題ありません。規格は次のように定義していますunsigned char*6.5/7memcpy7.21.2/1

この節のすべての関数について、各文字はunsigned char型であるかのように解釈されます(したがって、すべての可能なオブジェクト表現は有効であり、異なる値を持ちます)。

memcpy関数は、s2が指すオブジェクトからs1が指すオブジェクトにn文字をコピーします。オーバーラップするオブジェクト間でコピーが行われる場合、動作は定義されていません。

ただし、p後で使用する場合は、ビットパターンによっては未定義の動作が発生する可能性があります。そのような使用が起こらない場合、そのコードはCで問題ありません。


私の意見では、この問題についてははっきりとは言えないC ++標準によると、次のことが当てはまると思います。この解釈を唯一の可能性としてとらえないでください-漠然とした/不完全な仕様は推測の余地をたくさん残します。

タイプの位置合わせが適切でない可能性があるため、線(1)に問題があります。に格納されているオブジェクトのタイプをに変更します。後でそのオブジェクトにアクセスしない限り、エイリアシングルールは破られませんが、アライメント要件は破られる可能性があります。&punsignedpunsigned intp

ただし、 Lineには配置の問題がないため、後で(2)アクセスしない限り有効です。これにより、型が格納されたビットパターンをどのように解釈するかによって、未定義の動作が発生する可能性があります。それによってオブジェクトの種類が変わるとは思いません。pvoid*void*

長いGCCバグレポートがあり、そのようなキャストから生じたポインターを介した書き込みの影響と、配置構文の違いは何ですか(そのリストの人々はそれが何であるかについて同意していません)。

于 2010-07-18T11:47:09.167 に答える