7

多くの場合、C++ でコーディングする場合、coutステートメントを改行 ( \n) で終了します。ただし、私の本能は常に、この改行を文字列リテラル: として"\n"表現することでしたが、これは単一の文字であり、char リテラル ( ) としてより効率的に表現できます'\n'。例えば:

cout << "The value of var is " << var << "\n";

この現象には多くのコードがあります。したがって、質問は次のとおりです。

  1. 改行文字定数を表現する2つの異なる方法の効率に違いはありますか? 作成されたプログラムの実行に実際の違いが生じることについては心配していません (これは些細なことだと思います)。むしろ、どんなにわずかな効率であっても、理由もなく失われる可能性があることは、私を悩ませています。

  2. 文字列リテラル バージョンの効率が悪い場合、コンパイラはそれを文字定数バージョンに最適化しますか?

  3. にも精通していstd::endlます。ドキュメントには、「このマニピュレータは、単純な改行が必要な場合に誤って使用されることが多く、バッファリングのパフォーマンスが低下する」と記載されています。詳細については、この記事を参照してください。ただし、その記事では、言及されている「パフォーマンスの低下」はファイル I/O にのみ適用さendlれ、画面への書き込みに使用すると実際にパフォーマンスが向上する可能性があると述べています。これはどうしたの?

<<C++ 標準ライブラリを検索しましたが、関連する演算子のオーバーロードの実装が見つかりませんでした。私は宣言を見つけましたostream.tcc

extern template ostream& operator<<(ostream&, char);
extern template ostream& operator<<(ostream&, const char*);

しかし、実装でメカニズムがどのように煮詰まるかについての手がかりはありません。

これは何よりも理論的な問題であるため、「この 2 つの間に実質的な違いはない」と読むことに興味はありませ。そんなこと知ってる。違いがあるのか​​ 、コンパイラがそれをどのように処理するのか疑問に思っています。

4

3 に答える 3

3

おそらく、(コンパイル単位ごとに) 1 つの文字列に最適化されています。ほとんどのコンパイラは、「同じ内容の文字列をマージ」します。

単一の char 文字列へのポインターを渡すという事実を除けば、実際的な違いはほとんどないと思います。

あなたの具体的な質問に:

  1. はい、若干の違いがchar *あります。 には何らかの間接化が必要であり、実行する命令がいくつか余分に生成されるためです。コンソール出力 (ファイルへの出力ではなく) の場合は重要ではありません。全画面テキスト モードであっても、コンソールをスクロールすると 100 倍以上の命令が必要になるからです。
  2. 疑わしい。
  3. std::endl部分的なセクターまたはブロックがファイルに書き込まれているため、システムコールのオーバーヘッドが増加するため、バッファーをフラッシュします。これにより、ファイルへの出力が実際に減少します。を使用する場合"\n"、バッファ自体がいっぱいになるまで、ファイルはフラッシュされません。これは、少なくとも 512 バイト、場合によっては数十キロバイトになります。しかし、答え 1 については、コンソール出力のパフォーマンスは、画面のスクロール速度に大きく依存します。
于 2013-03-31T00:54:22.040 に答える
2

\n文字列リテラルとの違いendlは次のとおりです。

\nstdout に追加される文字列リテラルです。 endlまた、改行文字を標準出力に追加しますが、標準出力バッファもフラッシュします。そのため、より多くの処理が必要になる場合があります。これ以外には、実質的な違いはないはずです。

于 2013-03-31T00:52:33.857 に答える
1

メモリレイアウトが変更され(一方にはヌルターミネータがあり、もう一方にはありません)、リテラルの実際の型を変更する必要があるため(そして、拡張により、呼び出される関数を変更するため)、私はそれを強く疑っています)。したがって、ほとんどの場合、これは無効な変換であり、ごく少数の人にとっては十分な助けにはなりません。

とはいえ、コンパイラが十分に積極的なインライン化 (関数自体と定数データを関数にインライン化する) を行うと、事実上同じコードになる可能性があります。たとえば、Clang は以下をコンパイルします。

#include <iostream>
using namespace std;

int main() {
    cout << "X" << "\n";
    cout << "Y" << '\n';
}             

これに:

movq    std::cout@GOTPCREL(%rip), %rbx
leaq    L_.str(%rip), %rsi
movq    %rbx, %rdi
movl    $1, %edx
callq   std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
leaq    L_.str1(%rip), %rsi
movq    %rbx, %rdi
movl    $1, %edx
callq   std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
leaq    L_.str2(%rip), %rsi
movq    %rbx, %rdi
movl    $1, %edx
callq   std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
leaq    -9(%rbp), %rsi
movb    $10, -9(%rbp)
movq    %rbx, %rdi
movl    $1, %edx
callq   std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
xorl    %eax, %eax
addq    $8, %rsp
popq    %rbx
popq    %rbp

ご覧のとおり、インライン展開により、2 つのケースがほぼ同じになりました。(実際には'\n'、文字をスタックに置かなければならないため、ケースはもう少し複雑です。)

于 2013-03-31T00:49:11.737 に答える