この前の学期に受講したシステムプログラミングクラスでは、Cで基本的なクライアント/サーバーを実装する必要がありました。sock_addr_in
やcharバッファー(クライアントとサーバー間でデータを送受信するために使用した)教授は、bzero
ではなくmemset
のみを使用して初期化するように指示しました。彼はその理由を決して説明しませんでしたが、これに正当な理由があるかどうか興味がありますか?
ここに表示されます: http://fdiv.net/2009/01/14/memset-vs-bzero-ultimate-showdownbzero
の方が効率的これはメモリのゼロ化のみが行われるため、memset
が行う可能性のある追加のチェックを行う必要はありません。それでも、メモリをゼロにするためにmemset
を絶対に使用しない理由とは必ずしも思えません。
bzero
は非推奨と見なされ、さらに標準C関数ではありません。マニュアルによると、この理由から、memset
はbzero
よりも優先されます。では、なぜbzero
ではなくmemset
を使用したいのでしょうか?効率を上げるためだけですか、それともそれ以上ですか?同様に、memset
よりもbzero
の利点は、それを新しいプログラムの事実上の優先オプションにしますか?
bzero
よりもmemset
を好む理由はありません。
memset
は標準C関数ですが、bzero
はC標準関数ではありません。その理由は、おそらくmemset
関数を使用してまったく同じ機能を実現できるからです。
現在、効率性に関して、gcc
のようなコンパイラは、memset
の組み込み実装を使用しており、定数0
が検出されると特定の実装に切り替わります。ビルトインが無効な場合のglibc
についても同じです。
私はあなたが使用した(またはあなたの教師が影響を受けた)と推測しているNIX Network Programming by W. Richard Stevens。彼は、最新版であっても、bzero
の代わりにmemset
を頻繁に使用します。この本はとても人気があり、ネットワークプログラミングのイディオムになったと思います。
memset
は廃止され、移植性が低下するため、単純にbzero
に固執します。どちらかを使用することで実際の利益が得られるとは思いません。
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()
に展開されるマクロまたはインライン関数になります。
要するに:memset
は、bzero
よりも多くのアセンブリ操作が必要です。
これがソースです: http://fdiv.net/2009/01/14/memset-vs-bzero-ultimate-showdown
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インターフェースの数に関係なく、それらについて心配する必要はありません。心配する必要があるのは、呼び出しがバッファーをゼロにするという要件を満たしていることだけです。他の人は、どちらが他よりも好ましいかについて言及しているので、ここで停止します。
おそらくすべきではないbzero
を使用してください。実際には標準のCではなく、POSIXのものでした。
また、Wordは「だった」ことに注意してください。POSIX.1-2001ではdeprecated、POSIX.1-2008ではmemsetを尊重してremovedであったため、使用する方が良いでしょう標準C関数。
好きなように持ってください。 :-)
#ifndef bzero
#define bzero(d,n) memset((d),0,(n))
#endif
ご了承ください:
bzero
は何も返さず、memset
はvoidポインター(d
)を返します。これは、定義のvoidに型キャストを追加することで修正できます。#ifndef bzero
は、元の関数が存在する場合でも非表示にすることを妨げません。マクロの存在をテストします。これは多くの混乱を引き起こす可能性があります。bzero
を使用する場合、これは機能しません。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コンパイラによってキャッチされるためです。
memsetは3つのパラメーターを取り、bzeroは2つのメモリーを取ります。余分なパラメーターはさらに4バイトを必要とし、ほとんどの場合はすべてを0に設定するために使用されます