2012-09-23 12 views
6

私はいつも、ループの中に変数を定義するべきではないという印象を受けました。なぜこのバッファはループの内部で定義されていますか?

while (totalBytesRecvd < echoStrLen) 
{ 
    char buffer[BUFSIZE]; 
    numBytes = recv(sock, buffer, BUFSIZE - 1, 0); 
    ... 
    totalBytesRecvd += numBytes; 
    buffer[numBytes] = '\0'; 
    fputs(buffer, stdout); 
} 

のrecvのドキュメントを()、それはバッファポインタをどのように使用するかについては何も言及していない:これは、次のrecv()関数は、ループの反復ごとに新鮮な緩衝液を必要とする場合、私は思ってしまいます。理解を深めるために、私はループの直前でバッファを定義しようとしましたが、recv()はバッファを上書きするように見えます。これは、recv()がバッファの先頭にポインタを渡してから意味があります。

ループ内でバッファを何度も何度も定義する特別な理由はありますか?それとも私の基本的な理解は正しいのでしょうか?

答えて

8

recv,readなどの類似の関数は、バッファの以前の内容を気にせず、結果を書き込むためにのみ使用します。

バッファに初期化を行っていないため、変数がループのローカルとして宣言されていても、その内容は「未定義」になります。また

、ほとんどのCの実装上:初期化しない

  • そのターン中、それは同じ場所を取ることになりますことを意味し、その位置でスタック上であることを起こるものは何でも取る変数の手段これは以前の反復で、変数をループ外に置いた場合とまったく同じ結果が得られました。
  • スタックの割り当ては安いです - 一般にレジスタを調整するだけです。
  • 実際には、それらはさらに安価です:通常、レジスタ調整は関数の始めに行われ、すべてのローカル変数を考慮します。ローカル変数の有効範囲は、関数の開始時に割り当てられるため、コンパイル時の構造になります。あなたあなたの変数を初期化した場合

もちろん、その代わり、それは違うだろう - 初期化を実行するコードは、各繰り返しで実行する必要があります。しかし、上記のように、何も初期化する必要はありません。recvはバッファの現在の状態を気にしません。それが構築するために高価だ場合、ループ内の変数を定義する

+0

+1、私の答えよりもはるかに良い。 – Martin

+0

非常に参考になりました。誰も私のソースに私を指摘できますか?あるいは、コンパイラのドキュメントでこれをすべて見つけることができますか?コンパイラの最適化は、私が読んだCプログラミングの本で光りました。 – Nocturno

+2

私のソースは、コンパイラによって生成されたアセンブラを見過ぎるのに時間がかかります:)。一般に、この種の最適化については心配しないでください。適切なアルゴリズムに適切なものを使用して、コード*ニーズ*の最適化を実証することができれば、これらの種類の調整だけを検討してください。 – Martin

2

ループ内で変数を宣言すると、スタックスペースが確保されます。内容を消去したり、変数に触れたりしません。したがって、このスタイルの宣言は、ループ外で宣言するよりも高価ではありません。

5

無駄ではありません。この変数のスコープを宣言します。コンパイラは、このスコープからスタックからより多くを割り当てることができない他の目的のために、スタック上のスペースを再利用することがあります。実行時に余分な経費がかかりません。コンパイラはコンパイル時に必要なスタック領域を計算し、関数の先頭でスタックポインタを1回だけ調整します。

2

は悪いです、それはめったに最適化の最も基本的な、それも上のスタックポインタを変更する気にするつもりはないとC.

におけるケースませんループの各反復。多くのコンパイラは、デバッグモードで配列をゼロ初期化しますが、このバッファが巨大でなければ大きな問題にはなりません。

C++では、ループの外で一度しか構築できない場合は高価なコンストラクタで変数を宣言しないと考えるかもしれませんが、ここでは問題にはなりません。

1

私はいつも 変数をループ内に定義するべきではないという印象を受けてきました。これは、不要であるか無駄であるためです。

あなたはいつも間違った印象を受けていますが、基本的ではないだけでなく、非常に悪い習慣 - 時期尚早の最適化 - 非常に良いものに対して、可能。

1

私は、ループから宣言を移動すると、特に配列などの大きな構造の場合には、コードが高速になると考えていました。これはmalloc(ヒープ)データではよくあることだと思います。なぜなら、mallocを呼び出してループ内で自由にオーバーヘッドを無駄にすることができるからです。 (あなたのような)スタックデータの場合、私はこれを大きな問題とは思わない。

しかし、私は最近、内側のループから宣言を移動し、実際にのコードのコードに終わった反対の状況に遭遇しました。私は、このためのいくつかの可能性のある説明を思い付いた:

  1. 宣言は、より広い範囲に移動されたとき、コンパイラは同様に効果的コードを最適化することができませんでした。
  2. ループ反復の間に大量のデータがメモリに保持されていたため、キャッシュの使用効率が低下しました。

とにかくこれについては参考にはなりませんが、ループ内またはループ外の定義を動かすと状況に応じてコードが速くなるか遅くなる可能性があります。違いがあるかどうかを確認するために、コードを変更する前後のパフォーマンスを測定する必要があります。

+0

'また、ループから宣言を移動すると、特に配列などの大きな構造の場合には、コードが高速になると考えていました。 - *なぜ*あなたはそれを考えましたか? 'これはmalloc'd(ヒープ)データではよくあることだと思います - 割り当ては宣言ではありません! 'int foo = very_long_calculation(rand()); 'のようなステートメントをお持ちでしたら、ループ内で*計算*を動かすのにはコストがかかります。 mallocの呼び出しはどのように異なっていますか? –

関連する問題