2013-03-31 10 views
7

C++でコーディングするときに、改行(\n)でcoutステートメントを終了します。しかし、私の本能は常に文字列リテラルとしてこの改行を表現することでした。それは1文字であり、charリテラル('\n')としてより効率的に表現できますが、"\n"です。例えば :1文字の文字列リテラルは、シンプルなchar型リテラルに最適化されていますか?

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

コードのたくさんは、この現象にあります。だから、質問は次のとおりです。

  1. は、改行文字定数を表現する2つの異なる方法の効率いかなる違いはありますか?私は生産されたプログラムの実行に実際の差異をつけることに心配していません(私はそれは些細なことでしょう)。むしろ、何らかの効率は、わずかなものであっても、何の理由もなく失われるかもしれないということだけをバグします。

  2. 文字列リテラルのバージョンが効率的でない場合、コンパイラは完全に同じ動作を提供するため、文字定数バージョンに最適化しますか?

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

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

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

しかし、メカニックが実装で煮詰める方法などありません手掛かり。

これは何よりも理論的な質問のほうが多いので、私は読むことに興味がありません "praticalの違いはありません。そんなこと知ってる。私はちょうどそこに何か違いがあるかどうか、コンパイラがそれをどのように処理するのか不思議です。

+1

存在する「1つの文字列リテラル」は、空の文字列だけです。ゼロ終端記号を忘れないでください。 –

+0

'" \ n "'は 'const char [2]'型ですが、どのように*単一の文字に最適化できますか?また、あなたの質問3は無関係のようで、おそらく別の質問として投稿する必要があります。 – Praetorian

+2

OPは完全にストレージの違いを認識しています。それは最初の段落でカバーされています。 OPは、この特別な1文字の場合にc文字列がコンパイラによって置換されるかどうかを尋ねる。単一の文字はターミネータを必要としません。 – justin

答えて

3

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

私は実際の違いがほとんどないと思いますが、1つの文字列へのポインタを渡す以外は、あなたの具体的な質問へ

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

メモリレイアウトの変更(1つはヌルターミネータ、もう1つは変更しない)とリテラルの実際のタイプの変更が必要になることを強く疑う。関数と呼ばれます)。したがって、大部分のケースでは無効な変換であり、少数派の数え切れないほどの助けはありません。

つまり、コンパイラが十分なインライン展開(関数自体と関数への定数データのインライン展開)を行うと、実質的に同じコードが生成される可能性があります。例えば、クランは、次のようにコンパイルされます。

この中
#include <iostream> 
using namespace std; 

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

movq std::[email protected](%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'場合はもう少し複雑です)

+2

一方、私は 'printf(" \ n ")'が 'putc( '\ n')'に最適化されていることを覚えていますので、このような最適化には驚かないでしょう。 –

+0

'printf'はあらゆる種類の方法で最適化されたCの組み込み関数です。たとえば、 'printf(" blah \ n ")'は 'puts(" blah ")'に変わります。 – nneonneo

2

\nリテラルとendl文字列の間で異なってはということです:

\nは、その文字列リテラルでありますstdoutに追加されます。 endlは、改行文字をstdoutに追加しますが、stdoutバッファもフラッシュします。したがって、より多くの処理が必要になることがあります。これ以外には、実際的な違いはありません。

関連する問題