C ++での「printf」と「cout」の比較


回答:


333

この質問のすべての人が、質問が違いを尋ねただけでstd::coutprintf、それよりもはるかに優れていると主張していることに驚いています。今、違いstd::coutがあります-C ++であり、printfCです(ただし、Cの他のほとんどすべてと同様に、C ++ でも使用できます)。さて、私はここで正直になります。両方printfstd::coutその利点があります。

実際の違い

拡張性

std::cout拡張可能です。私は人々がそれprintfも拡張可能であると言うだろうことを知っていますが、そのような拡張はC標準では言及されていません(そのため、非標準機能を使用する必要があります-しかし、一般的な非標準機能さえ存在しません)、そのような拡張は1文字です(したがって、既存のフォーマットと競合するのは簡単です)。

とは異なりprintfstd::cout演算子のオーバーロードに完全に依存しているため、カスタム形式の問題はありませんstd::ostream。最初の引数として、2番目の型としてサブルーチンを定義するだけです。したがって、名前空間の問題はありません。クラス(1文字に限定されない)がある限り、そのクラスのstd::ostreamオーバーロードを機能させることができます。

しかし、多くの人が拡張したいと思うのではないかと思いますostream(正直なところ、簡単に作成できたとしても、そのような拡張機能はめったに見ませんでした)。ただし、必要な場合はここにあります。

構文

簡単にわかるように、printfstd::coutは異なる構文を使用しています。printfは、パターン文字列と可変長引数リストを使用した標準の関数構文を使用します。実際、printfCがそれらを持っている理由です- printf形式が複雑で、それらなしでは使用できません。ただし、std::cout別のAPI(operator <<自分自身を返すAPI)を使用します。

通常、これはCバージョンが短くなることを意味しますが、ほとんどの場合それは問題になりません。多くの引数を出力すると、違いが顕著になります。Error 2: File not found.エラー番号を想定してのようなものを記述する必要があり、その説明がプレースホルダーである場合、コードは次のようになります。どちらの例も同じように機能します(まあ、std::endl実際には、実際にバッファーをフラッシュします)。

printf("Error %d: %s.\n", id, errors[id]);
std::cout << "Error " << id << ": " << errors[id] << "." << std::endl;

これはそれほど奇妙に見えません(2倍長くなります)が、実際に引数をフォーマットするときは、単に出力するのではなく、さらに奇妙になります。たとえば、のようなものの印刷0x0424はただおかしいです。これは、std::cout状態と実際の値が混在していることが原因です。std::setfillタイプのような言語(もちろんC ++以外)を見たことがありません。printf引数と実際の型を明確に分離します。私は本当にprintfそれのバージョンと比較して(たとえそれが一種の不可解に見えても)それのバージョンを維持することを好みiostreamます(それはあまりにも多くのノイズを含んでいるため)。

printf("0x%04x\n", 0x424);
std::cout << "0x" << std::hex << std::setfill('0') << std::setw(4) << 0x424 << std::endl;

翻訳

これが本当の利点があるprintfところです。printfフォーマット文字列は...よく文字列です。これにより、のoperator <<乱用と比較して、翻訳が本当に簡単になりiostreamます。仮定すると、gettext()関数は変換し、表示したいError 2: File not found.、以前に示したフォーマット文字列の翻訳を取得するコードは次のようになります。

printf(gettext("Error %d: %s.\n"), id, errors[id]);

ここで、エラー番号が説明の後にあるフィクションに翻訳するとします。翻訳された文字列はのようになります%2$s oru %1$d.\n。さて、C ++でそれを行う方法は?さて、私にはわかりません。私はあなたが偽物作ることができますねiostream構築printfあなたに渡すことができgettext、翻訳の目的のために、または何かを。もちろん、$C標準ではありませんが、あまりにも一般的であるため、私の意見では安全に使用できます。

特定の整数型の構文を記憶/検索する必要がない

Cには多くの整数型があり、C ++にもあります。std::coutはすべての型を処理しますがprintf、整数型に応じて特定の構文が必要です(非整数型もありますが、実際に使用する唯一の非整数型printfconst char *(C文字列、)のto_cメソッドを使用して取得できますstd::string)。例えば、印刷するためにsize_tあなたが使用する必要があり、%zd一方で、int64_t使用する必要があります%"PRId64"。これらの表は、http://en.cppreference.com/w/cpp/io/c/fprintfおよびhttp://en.cppreference.com/w/cpp/types/integerで入手できます。

NULバイトを印刷することはできません。 \0

printfはC ++文字列ではなくC文字列を使用するため、特別なトリックがないとNULバイトを出力できません。特定の場合にはそれを使用することが可能です%c'\0'それは明らかにハックですが、引数として。

誰も気にしない違い

パフォーマンス

更新:それiostreamは非常に遅いので、ハードドライブよりも遅いことがわかります(プログラムをファイルにリダイレクトする場合)。stdio大量のデータを出力する必要がある場合は、との同期を無効にすると役立つ場合があります。(STDOUTに複数の行を書き込むのではなく)パフォーマンスが実際の問題である場合は、を使用しますprintf

誰もがパフォーマンスを気にかけていると思っていますが、誰もそれを測定する気になりません。とにかく、printfまたはを使用しても、I / Oがボトルネックになるというのが私の答えですiostream。私は考えるprintf ことができ(使用して打ち鳴らすしてコンパイルアセンブリに簡単に見てから速くなる-O3コンパイラオプションを)。私のエラー例を想定すると、例はprintf例よりもはるかに少ない呼び出しを行いcoutます。これはあるint mainprintf

main:                                   @ @main
@ BB#0:
        push    {lr}
        ldr     r0, .LCPI0_0
        ldr     r2, .LCPI0_1
        mov     r1, #2
        bl      printf
        mov     r0, #0
        pop     {lr}
        mov     pc, lr
        .align  2
@ BB#1:

2つの文字列と2(数値)がprintf引数としてプッシュされていることが簡単にわかります。それだけです。他には何もありません。比較のため、これはiostreamアセンブリにコンパイルされます。いいえ、インライン化はありません。すべてのoperator <<呼び出しは、別の引数のセットを持つ別の呼び出しを意味します。

main:                                   @ @main
@ BB#0:
        push    {r4, r5, lr}
        ldr     r4, .LCPI0_0
        ldr     r1, .LCPI0_1
        mov     r2, #6
        mov     r3, #0
        mov     r0, r4
        bl      _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l
        mov     r0, r4
        mov     r1, #2
        bl      _ZNSolsEi
        ldr     r1, .LCPI0_2
        mov     r2, #2
        mov     r3, #0
        mov     r4, r0
        bl      _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l
        ldr     r1, .LCPI0_3
        mov     r0, r4
        mov     r2, #14
        mov     r3, #0
        bl      _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l
        ldr     r1, .LCPI0_4
        mov     r0, r4
        mov     r2, #1
        mov     r3, #0
        bl      _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l
        ldr     r0, [r4]
        sub     r0, r0, #24
        ldr     r0, [r0]
        add     r0, r0, r4
        ldr     r5, [r0, #240]
        cmp     r5, #0
        beq     .LBB0_5
@ BB#1:                                 @ %_ZSt13__check_facetISt5ctypeIcEERKT_PS3_.exit
        ldrb    r0, [r5, #28]
        cmp     r0, #0
        beq     .LBB0_3
@ BB#2:
        ldrb    r0, [r5, #39]
        b       .LBB0_4
.LBB0_3:
        mov     r0, r5
        bl      _ZNKSt5ctypeIcE13_M_widen_initEv
        ldr     r0, [r5]
        mov     r1, #10
        ldr     r2, [r0, #24]
        mov     r0, r5
        mov     lr, pc
        mov     pc, r2
.LBB0_4:                                @ %_ZNKSt5ctypeIcE5widenEc.exit
        lsl     r0, r0, #24
        asr     r1, r0, #24
        mov     r0, r4
        bl      _ZNSo3putEc
        bl      _ZNSo5flushEv
        mov     r0, #0
        pop     {r4, r5, lr}
        mov     pc, lr
.LBB0_5:
        bl      _ZSt16__throw_bad_castv
        .align  2
@ BB#6:

ただし、正直なところ、I / Oがボトルネックであるため、これは何の意味もありません。それiostreamは「タイプセーフ」であるため、速くないことを示したかっただけです。ほとんどのC実装はprintf、計算されたgotoを使用してフォーマットを実装するため、printfコンパイラーが認識していなくても、可能な限り高速ですprintf(それらは認識さprintfれません-一部のコンパイラーは特定の場合に最適化できます-で終わる定数文字列\nは通常、に最適化されていますputs)。 。

継承

なぜ継承したいのかわかりませんが、ostream気にしません。それFILEも可能です。

class MyFile : public FILE {}

タイプセーフ

真の可変長引数リストには安全性はありませんが、printf警告を有効にすると、一般的なCコンパイラがフォーマット文字列の問題を検出できるため、問題ありません。実際、Clangは警告を有効にすることなくそれを行うことができます。

$ cat safety.c

#include <stdio.h>

int main(void) {
    printf("String: %s\n", 42);
    return 0;
}

$ clang safety.c

safety.c:4:28: warning: format specifies type 'char *' but the argument has type 'int' [-Wformat]
    printf("String: %s\n", 42);
                    ~~     ^~
                    %d
1 warning generated.
$ gcc -Wall safety.c
safety.c: In function main’:
safety.c:4:5: warning: format ‘%s expects argument of type char *’, but argument 2 has type int [-Wformat=]
     printf("String: %s\n", 42);
     ^

18
とにかく、I / Oがボトルネックであると言います。明らかに、その仮定をテストしたことはありません 「一方で、iostreamバージョンは75.3 MB / sで、ハードディスクに追いつくのに十分な速さでデータをバッファリングすることができません。それは悪いことであり、実際の作業さえまだ行っていません。私はしません。 I / Oライブラリーがディスクコントローラーを飽和させることができるはずだと言ったとき、私はあまりにも大きな期待を抱いているとは思いません。」
Ben Voigt 2014年

4
@BenVoigt:認めます、可能な場合はC ++を避けようとします。私はそれをたくさん使ってみましたが、私が使用した他のプログラミング言語よりも煩わしく、メンテナンスも困難でした。これは私がC ++を回避するもう1つの理由です-これはさらに高速ではありません(それはiostreamでもありません-ほとんどの実装ではC ++ライブラリ全体が低速ですがstd::sort、を除いて、qsort(2回)に比べて驚くほど高速です)実行可能サイズのコスト)。
Konrad Borowski、2014年

3
ここでは、coutを使用する場合の並列環境の問題について言及している人はいません。
ニコラスハミルトン

9
パフォーマンスの議論はまったく意味がありません。プログラムのアセンブリが増えても、プログラムが遅くなるわけではありません。大量のコードであるprintf関数を作成するすべてのコードを考慮していないためです。私の意見では、コンパイラは変数とフォーマットをよりよく理解できるため、printfよりもはるかに優れた<<演算子を使用してcoutを最適化することが可能です。
Ignas2526

18
私はこの答えについて多くのことが好きですが、おそらく私のお気に入りの部分は「誰もがパフォーマンスを気にかけていると思っていますが、誰もそれを測定する気にしない」でしょう。
カイルストランド

203

C ++よくある質問

[15.1]なぜ<iostream> 従来の代わりに使用する必要があるの<cstdio>ですか?

型の安全性を高め、エラーを減らし、拡張性を可能にし、継承性を提供します。

printf()間違いなく壊れていませんし、scanf()エラーが発生しやすいにもかかわらずおそらく住みやすいですが、どちらもC ++ I / Oが実行できることに関して制限されています。C ++ I / O(<<およびを使用>>)は、C(printf()およびを使用scanf())と比較して:

  • よりタイプセーフ:を使用する<iostream>と、I / Oされるオブジェクトのタイプがコンパイラーによって静的に認識されます。対照的に、<cstdio>「%」フィールドを使用してタイプを動的に把握します。
  • エラーが発生しにくい:を使用すると<iostream>、I / Oされる実際のオブジェクトと一貫している必要がある冗長な「%」トークンはありません。冗長性を削除すると、エラーのクラスが削除されます。
  • 拡張可能:C ++ <iostream>メカニズムにより、既存のコードを壊すことなく、新しいユーザー定義型をI / Oすることができます。みんなが同時に互換性のない新しい「%」フィールドをprintf()andに追加していた場合の混乱を想像してみて scanf()ください。
  • 継承可能:C ++ <iostream>メカニズムは、std::ostreamおよび などの実際のクラスから構築されstd::istreamます。異なり<cstdio>さん FILE*、これらは、実際のクラスとなり継承されています。これは、ストリームのように見えて動作する他のユーザー定義の機能を持つことができるが、それはあなたが望む奇妙で素晴らしいことは何でもすることを意味します。知らないユーザーによって書かれた何十億行ものI / Oコードを自動的に使用できます。ユーザーは、「拡張ストリーム」クラスについて知る必要はありません。

一方、printfは非常に高速でありcout非常に特殊で限定された場合優先して使用することが正当化される場合があります。常に最初にプロファイルを作成します。(たとえば、http://programming-designs.com/2009/02/c-speed-test-part-2-printf-vs-cout /を参照)


2
一方、型の安全性、表現力、パフォーマンスを同時に提供するFastFormatライブラリ(fastformat.org)があります。(私がまだ試したわけではありません...)
xtofl

3
@Marceloは、引用されているすべてのものを含む、良い要約であるためと考えられます。フォーマット...ええ、それはかなり悪いです。私はそれを自分で修正する必要がありましたが、他の人(あなた自身を含む)がそれを処理したようです。これは、もちろん、単なる愚痴よりも建設的です。
Mikeage

2
遅くともprintf()拡張可能であることになっています。udrepper.livejournal.com/20948.htmlにある
Maxim Egorushkin '25 / 11/11

4
@MaximYegorushkin:標準にprintfはそのような機能はありません。移植性のないライブラリメカニズムは、iostreamの完全に標準化された拡張性と同じレベルにはほとんどありません。
Ben Voigt 2014年

4
「その一方で、printfの方が大幅に高速です」printfもきれいで使いやすいので、可能な限りcoutを使用しないようにしています。
FluorescentGreen5

43

人々はしばしばそれprintfがはるかに速いと主張します。これは主に神話です。私はそれをテストしました、次の結果で:

cout with only endl                     1461.310252 ms
cout with only '\n'                      343.080217 ms
printf with only '\n'                     90.295948 ms
cout with string constant and endl      1892.975381 ms
cout with string constant and '\n'       416.123446 ms
printf with string constant and '\n'     472.073070 ms
cout with some stuff and endl           3496.489748 ms
cout with some stuff and '\n'           2638.272046 ms
printf with some stuff and '\n'         2520.318314 ms

結論:改行だけが必要な場合は、を使用しprintfます。それ以外の場合は、coutほぼ同じくらい高速です。詳細については、私のブログをご覧ください。

明確にするために、私はiostreamsが常にsより優れていると言っているわけではありませんprintf。私は、一般的な誤解を招く仮定に基づくワイルドな推測ではなく、実際のデータに基づいて情報に基づいた決定を行う必要があると言っているだけです。

更新:テストに使用した完全なコードは次のとおりです。g++追加のオプションなしでコンパイルされます(-lrtタイミングを除く)。

#include <stdio.h>
#include <iostream>
#include <ctime>

class TimedSection {
    char const *d_name;
    timespec d_start;
    public:
        TimedSection(char const *name) :
            d_name(name)
        {
            clock_gettime(CLOCK_REALTIME, &d_start);
        }
        ~TimedSection() {
            timespec end;
            clock_gettime(CLOCK_REALTIME, &end);
            double duration = 1e3 * (end.tv_sec - d_start.tv_sec) +
                              1e-6 * (end.tv_nsec - d_start.tv_nsec);
            std::cerr << d_name << '\t' << std::fixed << duration << " ms\n"; 
        }
};

int main() {
    const int iters = 10000000;
    char const *text = "01234567890123456789";
    {
        TimedSection s("cout with only endl");
        for (int i = 0; i < iters; ++i)
            std::cout << std::endl;
    }
    {
        TimedSection s("cout with only '\\n'");
        for (int i = 0; i < iters; ++i)
            std::cout << '\n';
    }
    {
        TimedSection s("printf with only '\\n'");
        for (int i = 0; i < iters; ++i)
            printf("\n");
    }
    {
        TimedSection s("cout with string constant and endl");
        for (int i = 0; i < iters; ++i)
            std::cout << "01234567890123456789" << std::endl;
    }
    {
        TimedSection s("cout with string constant and '\\n'");
        for (int i = 0; i < iters; ++i)
            std::cout << "01234567890123456789\n";
    }
    {
        TimedSection s("printf with string constant and '\\n'");
        for (int i = 0; i < iters; ++i)
            printf("01234567890123456789\n");
    }
    {
        TimedSection s("cout with some stuff and endl");
        for (int i = 0; i < iters; ++i)
            std::cout << text << "01234567890123456789" << i << std::endl;
    }
    {
        TimedSection s("cout with some stuff and '\\n'");
        for (int i = 0; i < iters; ++i)
            std::cout << text << "01234567890123456789" << i << '\n';
    }
    {
        TimedSection s("printf with some stuff and '\\n'");
        for (int i = 0; i < iters; ++i)
            printf("%s01234567890123456789%i\n", text, i);
    }
}

5
あなたのスコアでは、printfは簡単にcoutを打ちます(大多数の場合)。パフォーマンスに関しては、なぜcoutの使用をお勧めするのでしょうか。私はPERF同意するけど...現実的な例では、あまりにも違いはありません
mishal153

3
@ mishal153:パフォーマンスがそれほど変わらないと言っているだけなので、「coutは使用するのが遅いので絶対に使用しないでください」という一般的なアドバイスは、まったくばかげています。coutにはタイプセーフという明らかな利点があり、多くの場合、読みやすさもあります。(iostreamによる浮動小数点フォーマットは恐ろしいです...)
Thomas

35
重要な違いprintf()とは、std::ostreamということである単一のコール内のすべての引数が前者の出力に対し、std::ostreamそれぞれに別々の呼び出しを招きます<<。テストは1つの引数と改行のみを出力するため、違いがわかりません。
Maxim Egorushkin、

12
コンパイラーは、これらの呼び出しをインライン化できる必要があります。また、printfさまざまな書式指定子のヘルパー関数を隠蔽して多くの呼び出しを行う可能性があります...または、それは巨大なモノリシック関数です。繰り返しになりますが、インライン化のため、速度にまったく違いがありません。
トーマス

4
端末の時間を計った。sprintfまたはfprintfおよびstringstreamまたはを使用しfstreamます。
Ben Voigt 2014年

41

そして私は引用します

大まかに言えば、主な違いは型の安全性(cstdioにはない)、パフォーマンス(ほとんどのiostreamの実装はcstdioの実装よりも遅い)、および拡張性(iostreamはカスタム出力ターゲットとユーザー定義型のシームレスな出力を可能にする)です。


特にunixでは、POSIXではtypedefの1つが実際にどのようなサイズであるかわからないため、多くのキャストが必要か、プログラムの99%として%dでリスクを冒すだけです。%zがC99に付属するまでに長い時間を要しました。しかし、time_t / off_tの場合、正しいフォーマット命令の探求は続きます。
Lothar 2015

30

1つは、標準出力に出力する関数です。もう1つは、いくつかのメンバー関数とoperator<<その出力のオーバーロードをstdoutに提供するオブジェクトです。列挙できる違いは他にもたくさんありますが、あなたが何を求めているのかはわかりません。


12

私にとって、「printf」ではなく「cout」に移行する実際の違いは次のとおりです。

1)<<演算子は私のクラスに対してオーバーロードできます。

2)coutの出力ストリームは簡単にファイルに変更できます:(:copy paste :)

#include <iostream>
#include <fstream>
using namespace std;

int main ()
{
    cout << "This is sent to prompt" << endl;
    ofstream file;
    file.open ("test.txt");
    streambuf* sbuf = cout.rdbuf();
    cout.rdbuf(file.rdbuf());
    cout << "This is sent to file" << endl;
    cout.rdbuf(sbuf);
    cout << "This is also sent to prompt" << endl;
    return 0;
}

3)特に多くのパラメーターがある場合、coutはより読みやすくなります。

一つの問題では、cout書式設定オプションです。でデータ(精度、正当性など)をフォーマットするprintf方が簡単です。


1
いいね。一部の外部ライブラリスレッドで誰もこの方法でグローバルスレッドを変更していないことをどのようにして知ることができますか?
vp_arth 16

1
あなたは簡単に変更することができprintfて、それを交換することにより、同様のファイルにfprintf...
CoffeeTableEspresso

5

ここでは特に触れていないが、私が重要だと思う2つの点:

1)coutまだSTLを使用していない場合、手荷物が多くなります。オブジェクトファイルに2倍以上のコードを追加しますprintf。これは、string、これが私が自分の文字列ライブラリを使用する主な理由です。

2)coutオーバーロードされた<<演算子を使用していますが、これは残念です。これを使用している場合、混乱が生じる可能性があります。<<演算子を意図した目的(左シフト)に。個人的には、意図された用途に正接する目的でオペレーターに負荷をかけたくない。

結論:すでにSTLを使用している場合はcout(およびstring)を使用します。そうでなければ、私はそれを避ける傾向があります。


4

プリミティブを使用する場合は、どれを使用するかはまったく問題ではありません。便利なのは、複雑なオブジェクトを出力する場合です。

たとえば、クラスがある場合、

#include <iostream>
#include <cstdlib>

using namespace std;

class Something
{
public:
        Something(int x, int y, int z) : a(x), b(y), c(z) { }
        int a;
        int b;
        int c;

        friend ostream& operator<<(ostream&, const Something&);
};

ostream& operator<<(ostream& o, const Something& s)
{
        o << s.a << ", " << s.b << ", " << s.c;
        return o;
}

int main(void)
{
        Something s(3, 2, 1);

        // output with printf
        printf("%i, %i, %i\n", s.a, s.b, s.c);

        // output with cout
        cout << s << endl;

        return 0;
}

さて、上記はそれほど素晴らしいようには見えないかもしれませんが、コードの複数の場所でこれを出力する必要があるとします。それだけでなく、「int d」フィールドを追加するとします。coutを使用すると、一度変更するだけで済みます。ただし、printfでは、多くの場所で変更する必要があり、それだけでなく、出力する場所を思い出す必要があります。

そうは言っても、coutを使用すると、コードのメンテナンスに費やす時間を大幅に削減できます。また、新しいアプリケーションで「Something」オブジェクトを再利用する場合は、出力について心配する必要はありません。


また、パフォーマンスについては、アプリケーションがパフォーマンス向けに作成されている場合は何も出力しないでください。stdへのあらゆる種類の出力は、かなり高価で低速です。私はあなたがそれを避けるべきであると言い、そうすることが絶対に必要なときだけ出力します。
ダニエル

クラスには、外部から簡単にアクセスできないプライベートメンバーが含まれる場合があることに注意してください。出力演算子を使用すると、クラスと友だちになる必要がある場所が1つだけになり、知らないコードでも、どこにでも出力できるようになります。
hochl 2017

2

もちろん、メンテナンスを維持するために「何か」を少し上手に書くことができます。

#include <iostream>
#include <cstdlib>

using namespace std;

class Something
{
    public:
        Something(int x, int y, int z) : a(x), b(y), c(z) { }
        int a;
        int b;
        int c;

        friend ostream& operator<<(ostream&, const Something&);

        void print() const { printf("%i, %i, %i\n", a, b, c); }
};

ostream& operator<<(ostream& o, const Something& s)
{
    o << s.a << ", " << s.b << ", " << s.c;
    return o;
}

int main(void)
{
    Something s(3, 2, 1);

    // Output with printf
    s.print(); // Simple as well, isn't it?

    // Output with cout
    cout << s << endl;

    return 0;
}

そして、coutとprintfの少し拡張されたテストは、「double」のテストを追加しました。もし誰かがより多くのテストをしたいなら(Visual Studio 2008、実行可能ファイルのリリースバージョン):

#include <stdio.h>
#include <iostream>
#include <ctime>

class TimedSection {
    char const *d_name;
    //timespec d_start;
    clock_t d_start;

    public:
        TimedSection(char const *name) :
            d_name(name)
        {
            //clock_gettime(CLOCK_REALTIME, &d_start);
            d_start = clock();
        }
        ~TimedSection() {
            clock_t end;
            //clock_gettime(CLOCK_REALTIME, &end);
            end = clock();
            double duration = /*1e3 * (end.tv_sec - d_start.tv_sec) +
                              1e-6 * (end.tv_nsec - d_start.tv_nsec);
                              */
                              (double) (end - d_start) / CLOCKS_PER_SEC;

            std::cerr << d_name << '\t' << std::fixed << duration * 1000.0 << " ms\n";
        }
};


int main() {
    const int iters = 1000000;
    char const *text = "01234567890123456789";
    {
        TimedSection s("cout with only endl");
        for (int i = 0; i < iters; ++i)
            std::cout << std::endl;
    }
    {
        TimedSection s("cout with only '\\n'");
        for (int i = 0; i < iters; ++i)
            std::cout << '\n';
    }
    {
        TimedSection s("printf with only '\\n'");
        for (int i = 0; i < iters; ++i)
            printf("\n");
    }
    {
        TimedSection s("cout with string constant and endl");
        for (int i = 0; i < iters; ++i)
            std::cout << "01234567890123456789" << std::endl;
    }
    {
        TimedSection s("cout with string constant and '\\n'");
        for (int i = 0; i < iters; ++i)
            std::cout << "01234567890123456789\n";
    }
    {
        TimedSection s("printf with string constant and '\\n'");
        for (int i = 0; i < iters; ++i)
            printf("01234567890123456789\n");
    }
    {
        TimedSection s("cout with some stuff and endl");
        for (int i = 0; i < iters; ++i)
            std::cout << text << "01234567890123456789" << i << std::endl;
    }
    {
        TimedSection s("cout with some stuff and '\\n'");
        for (int i = 0; i < iters; ++i)
            std::cout << text << "01234567890123456789" << i << '\n';
    }
    {
        TimedSection s("printf with some stuff and '\\n'");
        for (int i = 0; i < iters; ++i)
            printf("%s01234567890123456789%i\n", text, i);
    }
    {
        TimedSection s("cout with formatted double (width & precision once)");
        std::cout << std::fixed << std::scientific << std::right << std::showpoint;
        std::cout.width(8);
        for (int i = 0; i < iters; ++i)
            std::cout << text << 8.315 << i << '\n';
    }
    {
        TimedSection s("cout with formatted double (width & precision on each call)");
        std::cout << std::fixed << std::scientific << std::right << std::showpoint;

        for (int i = 0; i < iters; ++i)
            { std::cout.width(8);
              std::cout.precision(3);
              std::cout << text << 8.315 << i << '\n';
            }
    }
    {
        TimedSection s("printf with formatted double");
        for (int i = 0; i < iters; ++i)
            printf("%8.3f%i\n", 8.315, i);
    }
}

結果は次のとおりです。

cout with only endl    6453.000000 ms
cout with only '\n'    125.000000 ms
printf with only '\n'    156.000000 ms
cout with string constant and endl    6937.000000 ms
cout with string constant and '\n'    1391.000000 ms
printf with string constant and '\n'    3391.000000 ms
cout with some stuff and endl    9672.000000 ms
cout with some stuff and '\n'    7296.000000 ms
printf with some stuff and '\n'    12235.000000 ms
cout with formatted double (width & precision once)    7906.000000 ms
cout with formatted double (width & precision on each call)    9141.000000 ms
printf with formatted double    3312.000000 ms

うわー、なぜendlそれほど効率的では'\n'ないのですか?
ニコラスハミルトン

1
これはendlバッファをフラッシュするためであり、フラッシュ\nしないためだと思いますが、これが明確な理由であるかどうかはわかりません。
Caleb Xu

これは質問への回答ではありません。ダニエルトーマスへの回答のようなものです。
Fabioはモニカを

2

C ++でスレッドを操作する場合、使用するcoutと興味深い結果が得られることを指摘しておきます。

このコードを考えてみましょう:

#include <string>
#include <iostream>
#include <thread>

using namespace std;

void task(int taskNum, string msg) {
    for (int i = 0; i < 5; ++i) {
        cout << "#" << taskNum << ": " << msg << endl;
    }
}

int main() {
    thread t1(task, 1, "AAA");
    thread t2(task, 2, "BBB");
    t1.join();
    t2.join();
    return 0;
}

// g++ ./thread.cpp -o thread.out -ansi -pedantic -pthread -std=c++0x

これで、出力はすべてシャッフルされます。結果も異なる可能性があります。数回実行してみてください。

##12::  ABABAB

##12::  ABABAB

##12::  ABABAB

##12::  ABABAB

##12::  ABABAB

あなたはprintfそれを正しくするために使うことができます、またはあなたは使うことができますmutex

#1: AAA
#2: BBB
#1: AAA
#2: BBB
#1: AAA
#2: BBB
#1: AAA
#2: BBB
#1: AAA
#2: BBB

楽しんで!


2
wtf threadは出力を大げさにしません。私は再現して、両方xyzABC出力で見つけました。そこは、W / BをマングリングされなかったABCとしてABABAB
Abhinav Gauniyal

1
coutスレッドの処理方法はわかりませんが、表示しているコードが、これらの出力を取得するために使用したコードではないことは確かです。あなたのコードでは、文字列を渡し"ABC"、スレッド1用と"xyz"、スレッド2のために、しかし、あなたの出力が示すAAABBB。混乱しているので修正してください。
Fabioはモニカ

1
cout<< "Hello";
printf("%s", "Hello"); 

どちらも値の印刷に使用されます。構文はまったく異なります。C ++には両方があり、Cにはprintfしかありません。


19
... 何?取り違えましたか
xtofl

1
問題を修正しました。-1修正が必要であり、答えは多くのことが望まれるために残されます。
Yacoby

3
関数名が逆になりました。coutはprintf構文で使用され、printfはcout構文で使用されました。受け入れられるべきではなかった!
Mahmoud Al-Qudsi 2010年

2
また、coutの主な欠点は、operator <<を使用することです。:)
2010年

8
これは確実に最良の回答ではありませんが、最良の回答として選ばれたという理由だけで、スキャットマンが彼の回答に対してどのように罰せられているのか理解できません。xbitの方がIMOに悪い答えを出しますが、投票は-1です。xbitがもう反対票を投じられるべきだと言っているわけではありませんが、OPのミスのためにscatmanを必要以上に反対票を投じることが公正であるとは思いません...
Jesse

1

拡張性の欠如printfは完全に真実ではありません
。Cでは、それは真実です。しかし、Cでは実際のクラスはありません。
C ++では、キャスト演算子をオーバーロードできるため、演算子をオーバーロードchar*して次のprintfように使用できます。

Foo bar;
...;
printf("%s",bar);

Fooが良い演算子をオーバーロードすると、可能になる可能性があります。またはあなたが良い方法を作った場合。要するに、私printfと同じくらい拡張可能coutです。

C ++ストリーム(一般的にはcoutだけではありません)で見られる技術的な引数は次のとおりです。

  • タイプセーフ。(そして、ちなみに、'\n'私が使用する1枚を印刷したいのであれば、私putchar('\n')は虫を殺すために原爆を使用しません。)

  • 学ぶのが簡単。(何もただの使用に、学ぶためのパラメータを「複雑ません」<<>>オペレーター)

  • ネイティブとの仕事std::string(のためにprintfそこにあるstd::string::c_str()が、用scanf?)

printf私は以下を参照してください。

  • より簡単な、または少なくとも短い(書かれた文字に関して)複雑なフォーマット。私にとってははるかに読みやすいです(好みの問題だと思います)。

  • 関数が作成したものをより適切に制御します(書き込まれた場所に%nフォーマッタがある文字数を返します。「何も出力されません。引数は、これまでに書き込まれた文字数が格納される、signed intへのポインタでなければなりません。」(printfから-C ++リファレンス

  • より良いデバッグの可能性。最後の引数と同じ理由で。

私の個人的な好みは、主に短い行が好きなため、およびテキストの印刷に関するタイプの問題を回避するのが本当に難しいとは思わないため、printf(およびscanf)関数に行きます。Cスタイルの関数で私が嘆く唯一のものstd::stringは、サポートされていないことです。私たちは、通過する必要char*にそれを与える前にprintf(とstd::string::c_str()私たちが読みたい場合は、しかし、どのように書き込みますか?)


3
コンパイラにはvarargs関数の型情報がないため、実際のパラメータは変換されません(標準の整数昇格などのデフォルトの引数昇格を除く)。5.2.2p7を参照してください。ユーザー定義の変換char*は使用されません。
Ben Voigt 2012

これが機能したとしても、それはsprintfの拡張性の例ではなく、sprintfが期待するものを提供するための巧妙なハックであり、どこにあるかなどのいくつかの深刻な問題を無視します char*あり、どこにどのくらいの期間、ユーザー定義の危険性ます暗黙のキャスト。
Marcelo Cantos、2015年

1

その他の違い:「printf」は整数値(印刷された文字数に等しい)を返し、「cout」は何も返しません

そして。

cout << "y = " << 7; アトミックではありません。

printf("%s = %d", "y", 7); アトミックです。

coutはタイプチェックを実行しますが、printfは実行しません。

同等のiostreamはありません "% d"


3
coutこれは関数ではなくオブジェクトであるため、何も返しません。operator<<何かを返します(通常は左側のオペランドですが、エラーがある場合はfalse値)。そして、どのような意味でprintf「アトミック」という呼び方ですか?
Keith Thompson、

9
原爆のようなものです。printf("%s\n",7);
アートレスノイズ2013

@artlessnoiseなぜセグメンテーションフォールトが失敗するのを待つのですか?%sですか?
Abhinav Gauniyal

1
それが「原爆」発言の要点です。printf %sの引数がnullで終わる文字列への有効なポインタを持っている必要があります。通常、メモリ範囲「7」(ポインタ)は無効です。セグメンテーション違反は幸運かもしれません。一部のシステムでは、「7」がコンソールに大量のゴミを出力することがあり、プログラムが停止する前に1日間それを確認する必要があります。つまり、これはの悪い点ですprintf。静的分析ツールは、これらの問題の多くを捉えることができます。
2016

技術的にprintfは型チェックを行いませんが、型エラーについて警告しないコンパイラを使用したことはありませんprintf...
CoffeeTableEspresso

1

TL; DR:ランダムなコメントをオンラインで信頼する前に、生成されたマシンコードサイズパフォーマンス読みやすさコーディング時間について、常に独自の調査を行ってください。

私は専門家ではありません。パフォーマンスの問題のために、組み込みシステムでC ++を使用しないようにする方法について話している2人の同僚を耳にしたばかりです。まあ、興味深いことに、実際のプロジェクトタスクに基づいてベンチマークを行いました。

上記のタスクでは、RAMに構成を書き込む必要がありました。何かのようなもの:

coffee = hot
sugar = none
milk = breast
mac = AA:BB:CC:DD:EE:FF

これが私のベンチマークプログラムです(はい、OPがfprintf()ではなくprintf()について質問したことを知っています。本質をキャプチャしてみてください。ところで、OPのリンクはとにかくfprintf()を指しています。)

Cプログラム:

char coffee[10], sugar[10], milk[10];
unsigned char mac[6];

/* Initialize those things here. */

FILE * f = fopen("a.txt", "wt");

fprintf(f, "coffee=%s\nsugar=%s\nmilk=%s\nmac=%02X:%02X:%02X:%02X:%02X:%02X\n", coffee, sugar, milk, mac[0], mac[1],mac[2],mac[3],mac[4],mac[5]);

fclose(f);

C ++プログラム:

//Everything else is identical except:

std::ofstream f("a.txt", std::ios::out);

f << "coffee=" << coffee << "\n";
f << "sugar=" << sugar << "\n";
f << "milk=" << milk << "\n";
f << "mac=" << (int)mac[0] << ":"
    << (int)mac[1] << ":"
    << (int)mac[2] << ":"
    << (int)mac[3] << ":"
    << (int)mac[4] << ":"
    << (int)mac[5] << endl;
f.close();

両方とも10万回ループする前に、それらを磨くために最善を尽くしました。結果は次のとおりです。

Cプログラム:

real    0m 8.01s
user    0m 2.37s
sys     0m 5.58s

C ++プログラム:

real    0m 6.07s
user    0m 3.18s
sys     0m 2.84s

オブジェクトファイルサイズ:

C   - 2,092 bytes
C++ - 3,272 bytes

結論:非常に特定のバージョンのLinuxカーネルを実行している非常に特定のプロセッサを備えた非常に特定のプラットフォームで、非常に特定のバージョンのGCCでコンパイルされたプログラムを実行して、非常に特定のタスクを実行します。。 C ++アプローチは、実行速度が大幅に向上し、読みやすさが大幅に向上するため、より適しています。一方、私の意見では、Cは小さなフットプリントを提供します。プログラムのサイズは私たちの関心事ではないため、ほとんど意味がありません。

Remeber、YMMV。


この例ではC ++がより読みやすいことに同意しません。これは、この例では複数の行が1つのprintf呼び出しにパックされるためです。これは、C ++コードを実行する方法よりも当然可読性が低く、読みにくく、保守が難しいため、Cで実行されることはほとんどありません。公平な比較は、Cを別々のprintfに広げます。
maharvey67 2018

1
@ maharvey67あなたの言ったことは本当です。ただし、Cで提供した例はパフォーマンスを考慮したものです。fprintfへの1つにパックされた呼び出しは、C ++の同等のものよりも2秒遅くなりました。Cコードを読みやすくすると、さらに遅くなる可能性があります。免責事項:これは1年前のことであり、CコードとC ++コードの両方を磨くために最善を尽くしたことを覚えています。fprintfを個別に呼び出した方が1つの呼び出しよりも高速であるという証明はありませんでしたが、この方法で実行した理由はおそらくそうではなかったことを示しています。
ウェズリー

0

私はプログラマーではありませんが、ヒューマンファクターエンジニアです。プログラミング言語は、学びやすく、理解し、使用しやすいものである必要があると感じています。これには、シンプルで一貫した言語構造が必要です。すべての言語はシンボリックであり、したがって、その核となる部分は任意ですが、慣習があり、それに従うことで、言語の学習と使用が容易になります。

C ++やその他の言語には、関数(パラメーター)として書かれた膨大な数の関数があります。 printf()この構文に従い、C ++の作成者がファイルを読み書きするための論理的に異なるメソッドを作成する場合は、同様の構文を使用して異なる関数を作成するだけで済みます。

Pythonではもちろんobject.method、変数もオブジェクトなので、かなり標準的な構文、つまりvariablename.print を使用して印刷できますが、C ++ではそうではありません。

<<演算子はルールに従っていないため、cout構文は好きではありません。これはメソッドまたは関数です。つまり、パラメーターを取り、それに何かを行います。ただし、数学的な比較演算子のように書かれています。これは、人的要因の観点からは不十分なアプローチです。


-1

printfは関数coutですが、は変数です。


6
答え自体は間違っているかもしれませんが、それでも本当の答えであるため、私はロールバックを行いました。(正しく)答えが間違っていると思われる場合、2つのオプションがあります。1)コメントを追加するか、2)新しい回答を追加します(または両方を実行します)。作者が意図したものとは完全に異なる何かを言うように誰かの答えを変更しないでください。
マーク・

1
printf 関数ですが printf()ですが、関数呼び出しです=)
vp_arth

coutはオブジェクトであり、変数ではありません。
Lin
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.