web-dev-qa-db-ja.com

C ++での 'printf'と 'cout'

C++で printf()cout の違いは何ですか?

311
hero

たとえ質問が違いを求めたとしても、この質問の誰もがstd::coutprintfよりずっと優れていると主張しているのは驚きです。今は違いがあります - std::coutはC++、そしてprintfはCです(ただし、Cの他ののように、C++でも使用できます)。さて、私はここで正直に言うでしょう。 printfstd::coutの両方に利点があります。

本当の違い

拡張性

std::coutは拡張可能です。私は人々がprintfも拡張可能であると言うことを知っていますが、そのような拡張はC標準では言及されていません(だから非標準的な機能を使う必要があるでしょう。レター(つまり、既存のフォーマットと競合するのは簡単です)。

printfとは異なり、std::coutはオペレータのオーバーロードに完全に依存するため、カスタムフォーマットに問題はありません - 最初の引数としてstd::ostreamを、次に2番目の型としてサブルーチンを定義するだけです。そのように、名前空間の問題はありません - あなたがクラスを持っている限り(それは1文字に制限されない)、あなたはそれのために働くstd::ostreamオーバーロードを持つことができます。

しかし、私は多くの人がostreamを拡張したいと思うことを疑います(正直に言うと、たとえ簡単に作成できるとしても、そのような拡張を見たことはめったにありません)。あなたがそれを必要とすれば、しかし、それはここにあります。

構文

容易に気付かれるかもしれませんが、printfstd::coutはどちらも異なる構文を使用します。 printfは、パターン文字列と可変長引数リストを使った標準の関数構文を使います。 printfは、Cがそれらを持っている理由です - 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フォーマット文字列は、文字列です。 iostreamoperator <<の悪用と比較して、それは翻訳を本当に簡単にします。 gettext()関数が変換され、Error 2: File not found.を表示したいと仮定すると、前に示したフォーマット文字列の翻訳を取得するためのコードは次のようになります。

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

それでは、エラー番号が説明の後にあるFictionishに変換するとしましょう。翻訳された文字列は%2$s oru %1$d.\nのようになります。それでは、C++でそれを行う方法は?まあ、わかりません。私はあなたがあなたがiostreamに渡すことができるprintfを構築する偽のgettextを作ることができると思います。もちろん、$はC標準ではありませんが、一般的なので、私の考えでは安全に使用できます。

特定の整数型の構文を覚えておく必要はありません。

Cにはたくさんの整数型があり、C++もそうです。 std::coutはすべての型を処理しますが、printfは整数型に応じた特定の構文を必要とします(非整数型がありますが、printfで実際に使用される唯一の非整数型はconst 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を使用してください。

誰もが彼らがパフォーマンスを気にすると考えていますが、誰もそれを測定しようとは思わない。私の答えは、printfiostreamのどちらを使用しても、とにかくI/Oがボトルネックになるということです。 printfは、(--- -O3コンパイラー・オプションを使用してclangを使用してコンパイルされた)Assemblyを簡単に調べることで速くなる可能性があると思います。私のエラーの例を想定すると、printfの例はcoutの例よりも少ない呼び出しをします。これはprintfを伴うint mainです。

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(number)がprintf引数としてプッシュされていることが簡単にわかります。それはそれについてです。他に何もありません。比較のために、これはAssemblyにコンパイルされた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:

ただし、正直に言うと、入出力がボトルネックになるため、これは意味がありません。 iostreamは「タイプセーフ」であるため、高速ではありません。ほとんどのC実装は計算gotoを使用してprintfフォーマットを実装しているので、コンパイラが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);
     ^
263
Konrad Borowski

C++ FAQ から:

[15.1]伝統的な<iostream>の代わりに<cstdio>を使うべきなのはなぜですか?

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

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

  • より安全な型:<iostream>を使うと、入出力されるオブジェクトの型はコンパイラによって静的に認識されます。対照的に、<cstdio>は "%"フィールドを使用して型を動的に把握します。
  • エラーが発生しにくい:<iostream>を使用すると、入出力される実際のオブジェクトと一致する必要がある冗長な「%」トークンがなくなります。冗長性を取り除くと、ある種のエラーが取り除かれます。
  • 拡張性:C++の<iostream>メカニズムにより、既存のコードを壊すことなく、新しいユーザー定義型を入出力することができます。全員がprintf()scanf()?に新しい互換性のない "%"フィールドを同時に追加したとしたら、混乱を想像してみてください。
  • 継承可能:C++の<iostream>メカニズムは、std::ostreamstd::istreamなどの実際のクラスから構築されています。 <cstdio>FILE*とは異なり、これらは実際のクラスなので継承可能です。これは、ストリームのように見え、動作する他のユーザー定義のものを持つことができるということを意味します。あなたは、あなたも知らないユーザによって書かれた無数のI/Oコードを自動的に使うようになります。そして、彼らはあなたの "拡張ストリーム"クラスについて知る必要はありません。

一方、printfはかなり高速であるため、very特定の限られたケースでは、coutより優先して使用するのが妥当な場合があります。常に最初にプロファイルします。 (例えば、 http://programming-designs.com/2009/02/c-speed-test-part-2-printf-vs-cout /]を参照してください)

193
Mikeage

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が常に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);
    }
}
38
Thomas

そして私は 引用

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

37
Kyle Rozendo

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

29
Marcelo Cantos

私にとっては、私が '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に関する1つの問題はフォーマットオプションです。 printf内のデータのフォーマット(精度、位置揃えなど)はより簡単です。

11
mishal153

ここで特に言及されていない2つの点は、私が重要だと思います。

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

2)coutはオーバーロードされた<<演算子を使っていますが、残念です。意図した目的で<<演算子も使用している場合は、これが混乱を招く可能性があります(左シフト)。私は個人的には、意図した用途に接する目的で演算子を過負荷にしたくありません。

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

5
Bill Weinman

プリミティブでは、どちらを使用してもかまいません。それが有用になるのは、複雑なオブジェクトを出力したいときです。

たとえば、クラスがあるとします。

#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"を追加したとしましょう。痛風で、あなたは一度だけそれを変更する必要があります。しかし、printfを使用すると、それを変更する必要がある可能性があります。それだけでなく、どの出力を出力するのかを自覚する必要があります。

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

4
Daniel

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
Apollo

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

#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
2
LuP

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

そして。

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

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

coutは型検査を行いますが、printfは行いません。

"% d"と同等の入出力ストリームはありません。

1
skan

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

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

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

C++ストリームについて見ることができる(一般的にはcoutだけではありません)技術的な議論は次のとおりです。

  • タイプセーフ。 (そして、ちなみに、私が1つの'\n'を印刷したいのなら、私はputchar('\n')...を使います。私は昆虫を殺すために核爆弾を使いません。).

  • 習得が簡単です。 (<<および>>演算子を使用するためだけに、学習する「複雑な」パラメーターはありません)

  • std::stringを使用してネイティブに作業します(printfにはstd::string::c_str()がありますが、scanfには?)

printfについては、

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

  • 関数の作成内容の制御を改善しました(書き込まれた文字数と%nフォーマッタがある文字数を返します。 printf - C++リファレンス

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

私の個人的な好みはprintf(そしてscanf)関数になります。これは主に短い行が大好きで、テキストの印刷時にタイプの問題を避けることが本当に難しいとは思わないからです。私がCスタイルの関数について私が嘆く唯一のことはstd::stringがサポートされていないということです。 printfに渡す前にchar*を通らなければなりません(読みたい場合はstd::string::c_str()と書きますが、書き方は?)

1
bmorel

私はプログラマーではありませんが、私はヒューマンファクターエンジニアです。プログラミング言語は習得が容易で理解しやすいものであるべきだと私は感じます、そしてこれはそれが単純で一貫した言語構造を持つことを必要とします。すべての言語は象徴的であり、したがって、その核心では任意ですが、規約があり、それに従うことで言語の習得と使用が容易になります。

C++やその他の言語では、関数(パラメータ)として書かれた膨大な数の関数があります。これは、コンピュータ以前の時代の数学の関数関係に元々使用されていた構文です。 printf()はこの構文に従います。もしC++の作者がファイルを読み書きするための論理的に異なる方法を作りたいのであれば、彼らは同じような構文を使って異なる関数を単に作成したかもしれません。

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

<<演算子は規則に従わないので、私はcout構文が好きではありません。それは方法または機能であり、すなわちそれはパラメータを取りそしてそれに対して何かをする。しかし、それはあたかもそれが数学的比較演算子であるかのように書かれています。これは人的要因の観点からは不適切なアプローチです。

1
Daniel Woodard

TL; DR:常に生成されたマシンコードサイズパフォーマンス読みやすさ、およびコーディング時間ランダムコメントを信頼するこれを含むオンライン。

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

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

コーヒー=暑い
砂糖=なし
ミルク=乳房
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は私の考えでは小さなフットプリントを提供していますが、プログラムサイズは私たちの関心事ではないのでほとんど意味がありません。

覚えておいて、YMMV。

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

両方とも値を印刷するために使用されます。それらは完全に異なる構文を持っています。 C++には両方があり、Cにはprintfしかありません。

0
scatman