web-dev-qa-db-ja.com

memsetでbzeroを使用する理由

この前の学期に受講したシステムプログラミングクラスでは、Cで基本的なクライアント/サーバーを実装する必要がありました。sock_addr_inやcharバッファー(クライアントとサーバー間でデータを送受信するために使用した)教授は、bzeroではなくmemsetのみを使用して初期化するように指示しました。彼はその理由を決して説明しませんでしたが、これに正当な理由があるかどうか興味がありますか?

ここに表示されます: http://fdiv.net/2009/01/14/memset-vs-bzero-ultimate-showdownbzeroの方が効率的これはメモリのゼロ化のみが行われるため、memsetが行う可能性のある追加のチェックを行う必要はありません。それでも、メモリをゼロにするためにmemsetを絶対に使用しない理由とは必ずしも思えません。

bzeroは非推奨と見なされ、さらに標準C関数ではありません。マニュアルによると、この理由から、memsetbzeroよりも優先されます。では、なぜbzeroではなくmemsetを使用したいのでしょうか?効率を上げるためだけですか、それともそれ以上ですか?同様に、memsetよりもbzeroの利点は、それを新しいプログラムの事実上の優先オプションにしますか?

147
PseudoPsyche

bzeroよりもmemsetを好む理由はありません。

memsetは標準C関数ですが、bzeroはC標準関数ではありません。その理由は、おそらくmemset関数を使用してまったく同じ機能を実現できるからです。

現在、効率性に関して、gccのようなコンパイラは、memsetの組み込み実装を使用しており、定数0が検出されると特定の実装に切り替わります。ビルトインが無効な場合のglibcについても同じです。

139
ouah

私はあなたが使用した(またはあなたの教師が影響を受けた)と推測しているNIX Network Programming by W. Richard Stevens。彼は、最新版であっても、bzeroの代わりにmemsetを頻繁に使用します。この本はとても人気があり、ネットワークプログラミングのイディオムになったと思います。

memsetは廃止され、移植性が低下するため、単純にbzeroに固執します。どちらかを使用することで実際の利益が得られるとは思いません。

63
austin

bzero()memset()よりもメモリをゼロに設定することの利点の1つは、ミスが発生する可能性が低いことです。

次のようなバグに何度も遭遇しました。

memset(someobject, size_of_object, 0);    // clear object

コンパイラーは文句を言いませんが(いくつかのコンパイラーでは警告レベルを上げるかもしれませんが)、メモリーがクリアされないという影響があります。これはオブジェクトを破壊しないので、ただ放っておきます-バグが明白なものに現れない可能性がかなりあります。

bzero()が標準ではないという事実は軽度の刺激です。 (FWIW、私のプログラムのほとんどの関数呼び出しが非標準であれば驚くことはありません。実際、そのような関数を書くことは私の仕事のようなものです)。

ここでの別の回答へのコメントで、アーロン・ニュートンは、スティーブンス他によるセクション1.2(強調追加)、Unixネットワークプログラミング、ボリューム1、第3版から以下を引用しました。

bzeroはANSI C関数ではありません。初期のBerkelyネットワーキングコードから派生しています。それでも、memsetは(3つの引数を持つ)bzeroよりも覚えやすい(2つの引数を持つ)ので、ANSI Cのmemset関数の代わりにテキスト全体で使用します。ソケットAPIをサポートするほぼすべてのベンダーもbzeroを提供しています。サポートされていない場合は、unp.hヘッダーでマクロ定義を提供します。

確かに、TCPv3 [TCP/IP Illustrated、Volume 3-Stevens 1996]の作者は、最初の印刷で10回出現するときに、memsetの2番目と3番目の引数を交換するミスを犯しました。両方の引数が同じ型であるため、Cコンパイラはこのエラーをキャッチできません。 (実際には、2番目の引数はintで、3番目の引数は通常size_tですが、通常はunsigned intですが、指定された値である0と16は、他のタイプの引数でも受け入れられます。)memsetへの呼び出しは引き続き機能しますというのは、実際にはインターネットソケットアドレス構造体の最後の8バイトを0に設定する必要があるソケット関数はごく少数であるからです。それでもエラーであり、bzeroを使用することで回避できます。関数プロトタイプが使用される場合、bzeroは常にCコンパイラによってキャッチされます。

また、memset()の呼び出しの大部分はメモリをゼロにすることだと考えているので、そのユースケースに合わせたAPIを使用してみませんか?

bzero()の潜在的な欠点は、コンパイラがmemcpy()が標準であるため、最適化する可能性が高いことです。ただし、最適化された不正なコードよりも正しいコードの方が優れていることに留意してください。ほとんどの場合、bzero()を使用しても、プログラムのパフォーマンスに顕著な影響はありません。また、bzero()は、memcpy()に展開されるマクロまたはインライン関数になります。

45
Michael Burr

要するに:memsetは、bzeroよりも多くのアセンブリ操作が必要です。

これがソースです: http://fdiv.net/2009/01/14/memset-vs-bzero-ultimate-showdown

4
Tal Bar

Bzero vs. memset引数について何か言及したかった。 ltraceをインストールして、ボンネットの下で行う動作を比較します。 libc6(2.19-0ubuntu6.6)を使用するLinuxでは、実行される呼び出しは(ltrace ./test123を介して)まったく同じです。

long m[] = {0}; // generates a call to memset(0x7fffefa28238, '\0', 8)
int* p;
bzero(&p, 4);   // generates a call to memset(0x7fffefa28230, '\0', 4)

で働いていない限り libcの深い腸 カーネル/ syscallインターフェースの数に関係なく、それらについて心配する必要はありません。心配する必要があるのは、呼び出しがバッファーをゼロにするという要件を満たしていることだけです。他の人は、どちらが他よりも好ましいかについて言及しているので、ここで停止します。

3
gumchew

おそらくすべきではないbzeroを使用してください。実際には標準のCではなく、POSIXのものでした。

また、Wordは「だった」ことに注意してください。POSIX.1-2001ではdeprecated、POSIX.1-2008ではmemsetを尊重してremovedであったため、使用する方が良いでしょう標準C関数。

3
paxdiablo

好きなように持ってください。 :-)

#ifndef bzero
#define bzero(d,n) memset((d),0,(n))
#endif

ご了承ください:

  1. 元のbzeroは何も返さず、memsetはvoidポインター(d)を返します。これは、定義のvoidに型キャストを追加することで修正できます。
  2. #ifndef bzeroは、元の関数が存在する場合でも非表示にすることを妨げません。マクロの存在をテストします。これは多くの混乱を引き起こす可能性があります。
  3. マクロへの関数ポインターを作成することはできません。関数ポインタを介してbzeroを使用する場合、これは機能しません。
2
Bruce

Memset関数の場合、2番目の引数はintであり、3番目の引数はsize_tです。

void *memset(void *s, int c, size_t n);

これは通常unsigned intですが、2番目と3番目の引数のそれぞれ0 and 16のような値が16と0のように間違った順序で入力された場合、memsetの呼び出しは機能しますが、何もしません。初期化するバイト数が0として指定されているため。

void bzero(void *s, size_t n)

このようなエラーは、bzeroを使用することで回避できます。これは、関数プロトタイプが使用されている場合、2つの引数をbzeroにスワップすると、常にCコンパイラによってキャッチされるためです。

1
havish

memsetは3つのパラメーターを取り、bzeroは2つのメモリーを取ります。余分なパラメーターはさらに4バイトを必要とし、ほとんどの場合はすべてを0に設定するために使用されます

0
Skynight