web-dev-qa-db-ja.com

C ++コードでprintfを使用する必要がありますか?

通常、coutcerrを使用して、コンソールにテキストを書き込みます。ただし、古くなったprintfステートメントを使用する方が簡単な場合があります。出力をフォーマットする必要があるときに使用します。

これを使用する場所の1つの例は次のとおりです。

// Lets assume that I'm printing coordinates... 
printf("(%d,%d)\n", x, y);

// To do the same thing as above using cout....
cout << "(" << x << "," << y << ")" << endl;

coutを使用して出力をフォーマットできることは知っていますが、printfの使用方法は既に知っています。 printfステートメントを使用しない理由はありますか?

67
Bob Dylan

cincoutを最初に学習し、その後printfを学習する生徒は、printf(または通常はfprintf)を圧倒的に好む。私自身、printfモデルが十分に読みやすいため、他のプログラミング言語に移植できました。 Olivier Danvy もあり、だれがそれをタイプセーフにしました。

printfの呼び出しを型チェックできるコンパイラがある場合、C++でfprintfとその友人を使用しない理由はありません。

免責事項:私はひどいC++プログラマです。

68
Norman Ramsey

プログラムの国際化を希望する場合は、iostreamに近づかないでください。問題は、iostreamで行われているように、文が複数のフラグメントで構成されている場合、文字列を適切にローカライズできないことです。

メッセージフラグメントの問題に加えて、順序付けの問題もあります。生徒の名前と成績の平均点を印刷するレポートを考えてみましょう。

std::cout << name << " has a GPA of " << gpa << std::endl;

これを別の言語に翻訳する場合、他の言語の文法では、名前の前にGPAを表示する必要がある場合があります。私の知る限り、iostreamsは補間された値を並べ替える方法がありません。

両方の長所(タイプセーフティとi18n対応)が必要な場合は、 Boost.Format を使用します。

48

適応性

POD以外のprintfを試行すると、未定義の動作が発生します。

struct Foo { 
    virtual ~Foo() {}
    operator float() const { return 0.f; }
};

printf ("%f", Foo());

std::string foo;
printf ("%s", foo);

上記のprintf呼び出しは、未定義の動作をもたらします。コンパイラーは実際に警告する場合がありますが、これらの警告は標準では必要ではなく、実行時にのみ認識されるフォーマット文字列では不可能です。

IOストリーム:

std::cout << Foo();
std::string foo;
std::cout << foo;

自分で判断してください。

拡張性

struct Person {
    string first_name;
    string second_name;
};
std::ostream& operator<< (std::ostream &os, Person const& p) {
    return os << p.first_name << ", " << p.second_name;
}

cout << p;
cout << p;
some_file << p;

C:

// inline everywhere
printf ("%s, %s", p.first_name, p.second_name);
printf ("%s, %s", p.first_name, p.second_name);
fprintf (some_file, "%s, %s", p.first_name, p.second_name);

または:

// re-usable (not common in my experience)
int person_fprint(FILE *f, const Person *p) {
    return fprintf(f, "%s, %s", p->first_name, p->second_name);
}
int person_print(const Person *p) {
    return person_fprint(stdout, p);
}

Person p;
....
person_print(&p);

Cで適切な呼び出し引数/署名を使用する際の注意点(例:person_fprint(stderr, ...person_fprint(myfile, ...)、C++では、「FILE- argument」は式から自動的に「派生」します。この派生のより正確な同等物は、実際には次のようになります。

FILE *fout = stdout;
...
fprintf(fout, "Hello World!\n");
person_fprint(fout, ...);
fprintf(fout, "\n");

I18N

Person定義を再利用します。

cout << boost::format("Hello %1%") % p;
cout << boost::format("Na %1%, sei gegrüßt!") % p;

printf ("Hello %1$s, %2$s", p.first_name.c_str(), p.second_name.c_str()); 
printf ("Na %1$s, %2$s, sei gegrüßt!", 
        p.first_name.c_str(), p.second_name.c_str()); 

自分で判断してください。

これは、今日(2017年)にはあまり意味がありません。たぶん直感的な感覚かもしれませんが、I18Nは平均的なCまたはC++プログラマーによって日常的に行われるものではありません。さらに、それはとにかく解剖学の痛みです。

性能

  1. Printfのパフォーマンスの実際の重要性を測定しましたか?ボトルネックアプリケーションは、計算結果の出力がボトルネックになるほど深刻な怠け者ですか?本当にC++が必要ですか?
  2. 恐ろしいパフォーマンスのペナルティは、printfとcoutを組み合わせて使用​​したい人を満足させることです。これは機能であり、バグではありません!

一貫してiostreamを使用する場合、次のことができます。

std::ios::sync_with_stdio(false);

そして、優れたコンパイラーで同等のランタイムを獲得します。

#include <cstdio>
#include <iostream>
#include <ctime>
#include <fstream>

void ios_test (int n) {
    for (int i=0; i<n; ++i) {
        std::cout << "foobarfrob" << i;
    }
}

void c_test (int n) {
    for (int i=0; i<n; ++i) {
        printf ("foobarfrob%d", i);
    }
}


int main () {
    const clock_t a_start = clock();
    ios_test (10024*1024);
    const double a = (clock() - a_start) / double(CLOCKS_PER_SEC);

    const clock_t p_start = clock();
    c_test (10024*1024);
    const double p = (clock() - p_start) / double(CLOCKS_PER_SEC);

    std::ios::sync_with_stdio(false);
    const clock_t b_start = clock();
    ios_test (10024*1024);
    const double b = (clock() - b_start) / double(CLOCKS_PER_SEC);


    std::ofstream res ("RESULTS");
    res << "C ..............: " << p << " sec\n"
        << "C++, sync with C: " << a << " sec\n"
        << "C++, non-sync ..: " << b << " sec\n";
}

結果 (g++ -O3 synced-unsynced-printf.cc./a.out > /dev/nullcat RESULTS):

C ..............: 1.1 sec
C++, sync with C: 1.76 sec
C++, non-sync ..: 1.01 sec

裁判官...自分。

いいえ、私のprintfを禁止しません。

Variadicテンプレートのおかげで、C++ 11でタイプセーフでI18Nに優しいprintfを使用できます。そして、ユーザー定義のリテラルを使用して、それらを非常に、非常に高性能にすることができます。つまり、完全に静的なインカネーションを書くことが可能になります。

概念実証があります 。当時、C++ 11のサポートは今ほど成熟していませんでしたが、アイデアは得られました。

時間的適応性

// foo.h
...
struct Frob {
    unsigned int x;
};
...

// alpha.cpp
... printf ("%u", frob.x); ...

// bravo.cpp
... printf ("%u", frob.x); ...

// charlie.cpp
... printf ("%u", frob.x); ...

// delta.cpp
... printf ("%u", frob.x); ...

後で、あなたがしなければならないデータが非常に大きくなります

// foo.h
...
    unsigned long long x;
...

これを維持し、バグをなくすことは興味深い演習です。特に、他の非結合プロジェクトがfoo.hを使用する場合。

その他。

  • バグの可能性:特にミックスでユーザー入力ベース文字列を投げる場合、printfでミスを犯すスペースがたくさんあります(I18Nチームのことを考えてください) 。このようなすべてのフォーマット文字列を適切にエスケープするように注意する必要があります。正しい引数などを必ず渡す必要があります。

  • IO-Streamsは私のバイナリを大きくします:これが保守性、コード品質、再利用性よりも重要な問題である場合、(問題を検証した後!) printfを使用します。

21
Sebastian Mach

Boost :: formatを使用します。型の安全性、std :: stringのサポート、printfのようなインターフェイス、coutを使用する機能、その他多くの優れた機能があります。あなたは戻りません。

19
janm

Fい<<cout<<構文。

19
Igor Zevaka

まったく理由はありません。古き良きCライブラリはまだ有効ですが、C++ライブラリのみを使用するように人々を駆り立てるのは、単なる奇妙なイデオロギーだと思います。私はC++の男で、C関数もよく使います。それらに問題はなかった。

7
mingos

ストリームは標準的な方法です。このコードをprintfで動作させてみてください:

template <typename T>
void output(const T& pX)
{
    std::cout << pX << std::endl;
}

幸運を。

つまり、型をostream 'sに出力できるようにする演算子を作成することができ、面倒なく他の型と同じように使用できます。 printfは、C++、またはより具体的にはテンプレートの一般性に適合しません。

使いやすさ以上のものがあります。一貫性もあります。私のすべてのプロジェクトで、cout(およびcerrclog)を使用してファイルに出力することもできます。 printfを使用する場合、すべてをスキップします。さらに、一貫性自体が良いことです。 coutprintfの混合は、完全に有効ですが、見苦しいです。

オブジェクトがあり、それを出力可能にしたい場合、これを行う最もクリーンな方法はオーバーロードoperator<<そのクラス。では、printfをどのように使用しますか? coutprintfが混在したコードになってしまいます。

本当にフォーマットが必要な場合は、ストリームインターフェイスを維持しながらBoost.Formatを使用します。一貫性およびフォーマット。

6
GManNickG

Printfを使用します。 C++ストリームを使用しないでください。 printfは、はるかに優れた制御(浮動小数点精度など)を提供します。通常、コードは短く、読みやすくなります。

Google C++スタイルガイド 同意します。

ロギングインターフェイスで必要な場合を除き、ストリームを使用しないでください。代わりにprintfのようなルーチンを使用してください。

ストリームの使用にはさまざまな長所と短所がありますが、この場合は、他の多くの場合と同様に、一貫性が議論に勝ります。コードでストリームを使用しないでください。

5
cuteCAT

全体的に私は同意します(特に複雑な書式設定が必要な場合は<<構文が嫌いです)

しかし、安全面を指摘する必要があります。

printf("%x",2.0f)
printf("%x %x",2)
printf("%x",2,2)

おそらくコンパイラーには気付かれませんが、アプリがクラッシュする可能性があります。

5
Martin Beckett

ニーズや好みに合ったものを使用してください。 printfに慣れている場合は、ぜひ使用してください。あなたがiostreamに満足しているなら、それらに固執してください。要件に最適な組み合わせと組み合わせてください。結局のところ、これはソフトウェアです。良い方法と悪い方法がありますが、たった1つの方法しかありません。

共有してお楽しみください。

4
Bob Jarvis

私はprintfが好きではありません。タイプセーフがないため、使用するのが危険になります。さらに、書式指定子を覚えておく必要があります。賢明に正しいことを行うテンプレート化された演算子は、はるかに優れています。したがって、私は常にC++でC++ストリームを使用します。

確かに、多くの人がotherの理由でprintfを好んでいます。

3
Paul Nathan

私はしばしばprintf()を使用することを「後戻り」しますが、より頻繁にsnprintf()を使用してフォーマットされた出力を容易にします。 C++でプログラミングするとき、このラッパーを使用します(上記の例を使用するため)cout << format("(%d,%d)\n", x, y);

ヘッダー(_stdiomm.h_)は次のとおりです。

_#pragma once

#include <cstdarg>
#include <string>

template <typename T>
std::basic_string<T> format(T const *format, ...);

template <typename T>
std::basic_string<T> vformat(T const *format, va_list args);
_

そしてソース(_stdiomm.cpp_):

_#include "stdiomm.h"
#include <boost/scoped_array.hpp>
#include <cstdio>

template <>
std::wstring vformat(wchar_t const *format, va_list arguments)
{
#if defined(_WIN32)
    int required(_vscwprintf(format, arguments));
    assert(required >= 0);
    boost::scoped_array<wchar_t> buffer(new wchar_t[required + 1]);
    int written(vswprintf(buffer.get(), required + 1, format, arguments));
    assert(written == required);
    return std::wstring(buffer.get(), written);
#else
#   error "No implementation yet"
#endif
}

template <>
std::string vformat(char const *format, va_list arguments)
{
#if defined(_WIN32)
    int required(_vscprintf(format, arguments));
    assert(required >= 0);
    boost::scoped_array<char> buffer(new char[required + 1]);
    int written(vsnprintf(buffer.get(), required + 1, format, arguments));
    assert(written == required);
    return std::string(buffer.get(), written);
#else
    char *buffer;
    int printed = vasprintf(&buffer, format, arguments);
    assert(printed != -1);
    std::string retval(buffer, printed);
    free(buffer);
    return retval;      
#endif
}

template <typename T>
std::basic_string<T> format(T const *format, ...)
{
    va_list ap;
    va_start(ap, format);
    std::basic_string<T> retval(vformat(format, ap));
    va_end(ap);
    return retval;
}

template std::wstring format(wchar_t const *format, ...);
template std::string format(char const *format, ...);
_

更新

他の答えをいくつか読んだ後、 boost::format() 自分に切り替える必要があるかもしれません!

3
Matt Joiner

質問はかなり古いものですが、2セントを加算したいと思います。

printf()を使用してユーザーが作成したオブジェクトを印刷する

考えてみると、これはかなり単純です-タイプを文字列化して、printfに文字列を送信できます。

std::string to_string(const MyClass &x)
{
     return to_string(x.first)+" "+to_string(x.second);
}

//...

printf("%s is awesome", to_string(my_object).c_str()); //more or less

残念なことはありませんでした(C++ 11 to_string()があります)オブジェクトを文字列化するための標準化されたC++インターフェイス...

printf()pitfall

単一のフラグ-%n

出力パラメーターである唯一のもの-intへのポインターが必要です。正常に書き込まれた文字数を、このポインターが指す場所に書き込みます。これを上手に使用すると、セキュリティの脆弱性であるオーバーランを引き起こす可能性があります(printf()形式文字列攻撃を参照)。

1
milleniumbug

fmt library を使用すると、両方の長所を最大限に活用できます。これは、iostreamの安全性と拡張性と、(s)printf。例:

std::string = fmt::format("The answer is {}", 42);

このライブラリは、Pythonに似たprintf形式の文字列構文をサポートしています。

免責事項:私は fmt library の著者です。

1
vitaut

それは状況次第です。何も完璧ではありません。私は両方を使用します。 ostreamで>>演算子をオーバーロードできるため、ストリームはカスタムタイプに適しています。ただし、間隔などについては、printf()を使用することをお勧めします。 stringstreamなどは、Cスタイルのstrcat()よりも優れています。そのため、状況に適したものを使用してください。

0
quantum

C++streamsは、実際にはオーバーロードされた演算子<<
printfはC wayであるため、ストリームはC++ wayであると何度も読みましたが、どちらもライブラリ機能ですC++。したがって、最適なものを使用する必要があります。
私は主にprintfを好みますが、よりクリーンなコードを提供し、プレースホルダーと引数を一致させる必要がないようにするストリームも使用しました。

0
Petruza

Coutとcerrはマルチスレッドには安全ではないという警告を読みました。 trueの場合、これを使用しないようにする正当な理由です。注:openMPではGNU g ++を使用します。

0
Oleg Shalaev

ほとんどの場合、一時的なデバッグステートメントにはprintfを使用します。より永続的なコードの場合は、The C++ Wayのように 'c'ストリームを好みます。 boost :: formatは有望に見え、ストリームの使用を置き換える可能性がありますが(特に複雑にフォーマットされた出力の場合)、恐らく長い間printfに代わるものはないでしょう。

0
aib