0

私は数日前に cout がバッファを持っていることを知っています。ググると、バッファはスタックのようなもので、cout と printf の出力を右から左に取得し、(コンソールまたはファイルに)出力すると言われています。 )上から下へ。このような、

a = 1; b = 2; c = 3;
cout<<a<<b<<c<<endl;
buffer:|3|2|1|<-   (take “&lt;-” as a poniter)

output:|3|2|<-     (output 1)
        |3|<-       (output 2)
        |<-         (output 3)

次に、以下のコードを記述します。

#include <iostream> 
using namespace std; 
int c = 6;
int f() 
{   
    c+=1; 
    return c; 
} 

int main() 
{ 
     int i = 0; 
     cout <<"i="<<i<<" i++="<<i++<<" i--="<<i--<<endl; 
     i = 0;
     printf("i=%d i++=%d i--=%d\n" , i , i++ ,i-- );

     cout<<f()<<" "<<f()<<" "<<f()<<endl; 
     c = 6;
     printf("%d %d %d\n" , f() , f() ,f() );
     system("pause");
     return 0; 
}

VS2005では、出力は

i=0 i++=-1 i--=0
i=0 i++=-1 i--=0
9 8 7
9 8 7

スタックのやり方は合ってるみたいですね〜 ただ、昨日C++ Primer Plusを読んでみると、coutは左から右に動き、毎回オブジェクト(cout)を返すって書いてあったので、「それが出力を連結できる機能です」挿入」を使用して。しかし、左から右への方法では説明できません cout<

するとAlnitakは、「<< 演算子は実際には ostream& operator<<(ostream& os, int) であるため、別の書き方では次のようになります: operator<< ( operator<< ( operator<< ( cout, a ), b ), c)",

右端の引数が最初に評価される場合、それはある程度説明できます。

cout のバッファの仕組みについて混乱しています。誰か助けてもらえますか?

4

5 に答える 5

11

あなたはたくさんのものを混ぜています。現在まで:

  • の実装詳細cout
  • 連鎖呼び出し
  • 呼び出し規約

それらを個別に読んでみてください。そして、それらすべてを一度に考えないでください。

printf("i=%d i++=%d i--=%d\n" , i , i++ ,i-- );

上記の行は、未定義の動作を呼び出します。FAQ 3.2を読んでください。あなたが観察しているのは、関数の呼び出し規約と、特定の実装 (つまり、あなたのもの) によってスタックにパラメータが渡される方法の副作用であることに注意してください。他のマシンで作業していた場合、これが同じであるとは限りません。

関数呼び出しの順序とバッファリングを混同していると思います。coutステートメントの後に複数の挿入がある場合<<、実際には複数の関数呼び出しを次々と呼び出しています。したがって、次のように書くとします。

cout << 42 << 0;

それは本当に意味します:あなたは電話します、

cout = operator<<(cout, 42)

次に、同じ演算子への別の呼び出しで return を次のように使用します。

cout = operator<<(cout, 0)

cout上記でテストしたことは、の内部表現を何も教えてくれません。詳細については、ヘッダー ファイルを参照することをお勧めします。

于 2009-03-20T06:10:21.413 に答える
4

一般的なヒントとして、i または i-- の別の使用法と同じ行で i++ を決して使用しないでください。

問題は、関数の引数が任意の順序で評価される可能性があるため、関数の引数に副作用 (インクリメント操作やデクリメント操作など) がある場合、それらが期待どおりの順序で動作することを保証できないことです。これは避けるべきことです。

この場合も同じことが言えます。これは、cout 使用量の実際の拡張に似ています。

function1 ( function2 ( foo ), バー );

コンパイラは、function2 を呼び出す前に bar を自由に評価できます。逆も同様です。たとえば、 function1 が呼び出される前に function2 が戻ることは保証できますが、それらの引数が特定の順序で評価されることは保証できません。

これは、次のようなことをすると問題になります。

function1 ( function2 ( i++), i );

「i」が「i++」の前または後に評価されるかどうかを指定する方法がないため、予想とは異なる結果が得られる可能性が高く、異なるコンパイラーまたは同じコンパイラーの異なるバージョンでさえ異なる結果が得られる可能性があります.

要するに、副作用のあるステートメントは避けてください。それらが行の唯一のステートメントである場合、または同じ変数を一度だけ変更することがわかっている場合にのみ、それらを使用してください。(「行」とは、単一のステートメントとセミコロンを意味します。)

于 2009-03-20T06:33:54.557 に答える
1

表示されるのは未定義の動作です。

ローカルiおよびグローバルcは、シーケンス ポイントなしで複数回加算/減算されます。つまり、得られる値は何でもかまいません。コンパイラに依存し、おそらくプロセッサのアーキテクチャとコアの数にも依存します。

バッファーはcoutキューと考えることができるので、Alnitak は正しいです。

于 2009-03-20T06:20:19.813 に答える
1

未定義の動作が見られることを正しく指摘する他の回答に加えて、std::cout型のオブジェクトを使用std::streambufして内部バッファリングを行うことに言及したいと思いました。基本的に、これはバッファを表す抽象クラスです (サイズは実装に固有であり、バッファリングされていないストリーム バッファの場合は 0 になることもあります)。forstd::coutは、「オーバーフロー」すると標準出力にフラッシュされるように記述されています。

実際、std::streambuf関連付けられているものstd::cout(またはそのことについては任意のストリーム) を変更できます。std::coutこれは、すべての呼び出しをログ ファイルなどで終了させるなど、巧妙なことをしたい場合に便利です。

そして、あなたが呼び出し規約を他の詳細と混同していると言ったように、それらは std::cout のバッファリングとはまったく関係ありません。

于 2009-03-20T07:32:57.033 に答える