web-dev-qa-db-ja.com

memcpy()の代わりに代入してCで構造をコピーする

最近まで、構造化フィールドのコピーがmemcpy()で行われるのを見てきました。クラスとオンラインの手順では、1つの構造体の内容を別の構造体にコピーすると、通常は次のようになります。

_struct block *b0 = malloc(sizeof(struct block));
struct block *b1 = malloc(sizeof(struct block));
/* populate fields in *b0 */
memcpy(b1, b0, sizeof *b1); /* copy contents of b0 into b1 */
/* free b0, b1 */
_

ただし、このタスクは、memcpy()を置き換える簡単な割り当てでも実行できます。

_*b1 = *b0; /* dereferenced struct assignment */
_

これが(少なくとも私の限られた経験では)広く使用されていない理由はありますか?これらの2つのメソッド(代入とmemcpy())は同等ですか、または一般にmemcpy()を使用する説得力のある理由はありますか?

22
olliezhu

どちらの方法も同等であり、浅いコピーを実行します。つまり、構造自体はコピーされますが、構造が参照するものはコピーされません。

なぜmemcpyの方が人気があるのか​​はわかりません。古いバージョンのCは構造体の割り当てをサポートしていません( 1978年には一般的な拡張機能でしたが )。したがって、より移植性のあるコードを作成する方法としてmemcpyスタイルがスタックしているのではないでしょうか?いずれにせよ、PCコンパイラでは構造体の割り当てが広くサポートされており、memcpyを使用するとエラーが発生しやすくなります(サイズが間違っていると、悪いことが発生する可能性があります)。したがって、構造体の割り当てを使用することをお勧めします可能であれば。

ただし、memcpyのみが機能する場合があります。例えば:

  • 構造化されていないバッファーとの間で構造体をコピーする場合(たとえば、ディスクに保存/ロードしたり、ネットワークで送受信したりする場合)memcpyを使用する必要があります。構造体の割り当てにはソースと適切に位置合わせされる宛先。
  • 構造体の後に追加情報をパックする場合、おそらく ゼロ要素配列を使用 を使用し、memcpyを使用して、この追加情報をサイズフィールドに含める必要があります。
  • 構造体の配列をコピーする場合は、構造体をループしてコピーするよりも、単一のmemcpyを実行する方がmayより効率的です個別に。それでも、そうではないかもしれません。 memcpy実装のパフォーマンス特性は異なります。
  • 一部の組み込みコンパイラは、構造体の割り当てをサポートしていない場合があります。もちろん、問題のコンパイラーがサポートしていない他のより重要なものももちろんあります。

Cではmemcpyと構造体の割り当ては通常同等ですが、C++ではmemcpyと構造体の割り当てはnotであることにも注意してください同等。一般的にC++では、構造体の割り当てがオーバーロードされてディープコピーや参照カウント管理などの追加の処理を実行できるため、memcpying構造体を回避するのが最善です。

31
bdonlan

これはあなたが探している正確な答えにはなりません。

私が出会ったシナリオを説明しています。

memcpy()を使用すると、宛先にバイト単位でコピーされます。したがって、ARMアーキテクチャではデータのアライメントについて心配する必要はありません。=演算子、およびアドレスのいずれかが4バイトにアラインされていない場合、アラインメントエラーが発生します。

From Arm site:

書き込まれた最後のバイトを1バイト超えた宛先位置へのポインター。これにより、書き込みプロセスを継続できますwith perfect alignment of bytesメモリブロックの文字列連結。

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0175k/Cihbbjge.html

6
Jeyaram

答えが説明しないので、この古い質問を復活させますなぜmemcpyが実際に優先されます。

memcpyは、プログラマーが単にポインターではなくコンテンツをコピーしたいことを明確にするため、推奨されます。

次の例では、2つの割り当ては2つの非常に異なることを行います。

struct Type *s1,*s2;
*s1=*s2;
s1=s2;

誤って一方を他方の代わりに使用すると、悲惨な影響が生じる可能性があります。コンパイラーは文句を言いません。初期化されていないポインタが使用されたときにプログラムがクラッシュしない限り、エラーは長時間気付かれないままになり、奇妙な副作用を引き起こす可能性があります。

次のいずれかとして記述します。

memcpy(s1,s2,sizeof(*s1));
memcpy(s1,s2,sizeof(*s2));
memcpy(s1,s2,sizeof(struct Type));

意図はコンテンツをコピーすることであることを読者に知らせてください(タイプセーフと境界チェックを犠牲にして)。

一部のコンパイラ(gccなど)は、次のような状況に遭遇すると、sizeofに関する警告を発行します。

memcpy(s1,s2,sizeof(s1));
4

一部の人々はmemcpyを好みます。なぜなら、それが彼らが学んだことであり、彼らが割り当てだけを行うことができることを理解しなかったからです(古代には割り当ては許可されませんでしたが、それはずっと前のことです)。 malloc()によって割り当てられたメモリは常に正しく整列されるため、心配する必要のある整列の問題はありません。また、コンパイラはこの割り当てをmemcpy呼び出しに簡単に変換できるため、memcpyよりもコードが遅くなったり、コードが増えたりすることはありません。もちろん、時代遅れのコンパイラが組み込まれた組み込みシステムもあります。

0
gnasher729