34

新しいC++コードでは、Cstdioライブラリの代わりにC++iostreamライブラリを使用する傾向があります。

一部のプログラマーはstdioに固執しているようで、よりポータブルであると主張していることに気づきました。

これは本当に本当ですか?何を使うのが良いですか?

4

13 に答える 13

39

元の質問に答えるには:
stdioを使用して実行できることはすべて、iostreamライブラリを使用して実行できます。

Disadvantages of iostreams: verbose
Advantages    of iostreams: easy to extend for new non POD types.

C上で行われたC++の前進は、型安全性でした。

  • iostreamsは、明示的にタイプセーフになるように設計されています。したがって、オブジェクトへの割り当てでは、割り当てられているオブジェクトのタイプ(コンパイラ時)も明示的にチェックされます(必要に応じてコンパイル時エラーが発生します)。したがって、実行時のメモリのオーバーランや、charオブジェクトへのfloat値の書き込みなどを防ぎます。

  • 一方、scanf()/ printf()とファミリは、プログラマーがフォーマット文字列を正しく取得することに依存しており、型チェックはありませんでした(gccには役立つ拡張機能があると思います)。結果として、それは多くのバグの原因でした(プログラマーはコンパイラーよりも分析が完璧ではないため[コンパイラーは人間よりも完璧であるとは言えません])。

コリン・ジェンセンからのコメントを明確にするためだけに。

  • iostreamライブラリは、最後の標準のリリース以来安定しています(実際の年は忘れていますが、約10年前です)。

ミカエル・ヤンソンのコメントを明確にするため。

  • 彼が言及しているフォーマットスタイルを使用する他の言語には、実行時のクラッシュを引き起こす可能性のあるC stdioライブラリの危険な副作用を防ぐための明示的な保護手段があります(Cでは言及されていません)。

NB私はiostreamライブラリが少し冗長な側にあることに同意します。しかし、私は実行時の安全性を確保するために冗長性を我慢するつもりです。ただし、 Boost Format Libraryを使用すると、冗長性を軽減できます。

#include <iostream>
#include <iomanip>
#include <boost/format.hpp>

struct X
{  // this structure reverse engineered from
   // example provided by 'Mikael Jansson' in order to make this a running example

    char*       name;
    double      mean;
    int         sample_count;
};
int main()
{
    X   stats[] = {{"Plop",5.6,2}};

    // nonsense output, just to exemplify

    // stdio version
    fprintf(stderr, "at %p/%s: mean value %.3f of %4d samples\n",
            stats, stats->name, stats->mean, stats->sample_count);

    // iostream
    std::cerr << "at " << (void*)stats << "/" << stats->name
              << ": mean value " << std::fixed << std::setprecision(3) << stats->mean
              << " of " << std::setw(4) << std::setfill(' ') << stats->sample_count
              << " samples\n";

    // iostream with boost::format
    std::cerr << boost::format("at %p/%s: mean value %.3f of %4d samples\n")
                % stats % stats->name % stats->mean % stats->sample_count;
}
于 2008-09-23T04:52:19.813 に答える
17

それはあまりにも冗長です。

次のことを行うための iostream 構造を熟考してください (scanf の場合と同様)。

// nonsense output, just to examplify
fprintf(stderr, "at %p/%s: mean value %.3f of %4d samples\n",
    stats, stats->name, stats->mean, stats->sample_count);

それには次のようなものが必要です。

std::cerr << "at " << static_cast<void*>(stats) << "/" << stats->name
          << ": mean value " << std::precision(3) << stats->mean
          << " of " << std::width(4) << std::fill(' ') << stats->sample_count
          << " samples " << std::endl;

文字列の書式設定は、文字列に埋め込まれた書式設定 DSL を支持して、オブジェクト指向性を回避できるケースであり、回避する必要があります。Lisp のformat、Python の printf スタイルの書式設定、または PHP、Bash、Perl、Ruby およびそれらの文字列補間を検討してください。

iostreamそのユースケースはせいぜい見当違いです。

于 2008-09-23T04:32:44.150 に答える
14

Boost Format Libraryは、printfスタイルの文字列フォーマットのタイプセーフでオブジェクト指向の代替手段を提供し、演算子%の巧妙な使用による通常の冗長性の問題に悩まされないiostreamを補完します。iostreamの演算子<<を使用したフォーマットが嫌いな場合は、プレーンCのprintfを使用するよりも検討することをお勧めします。

于 2008-09-23T06:15:07.273 に答える
9

古き良き時代に戻って、C ++標準委員会は言語をいじくり回し続け、iostreamsは動くターゲットでした。iostreamを使用した場合は、毎年かそこらでコードの一部を書き直す機会が与えられました。そのため、1989年から大きく変わっていないstdioをいつも使っていました。

今日何かをしているとしたら、iostreamを使用します。

于 2008-09-23T04:25:14.730 に答える
7

私のように、C ++を学ぶ前にCを学んだ場合、stdioライブラリを使用する方が自然に思えます。iostreamとstdioには長所と短所がありますが、iostreamを使用する場合はprintf()がありません。

于 2008-09-23T04:23:51.677 に答える
5

原則として、私はiostreamを使用しますが、実際には、フォーマットされた小数が多すぎてiostreamが読みにくくなるなどの理由で、stdioを使用します。Boost :: formatは改善されていますが、私にとっては十分な動機付けにはなりません。実際には、ほとんどの最新のコンパイラはとにかく引数チェックを行うため、stdioはほぼタイプセーフです。

それは私がまだ解決策のどれにも完全に満足していない領域です。

于 2008-09-30T20:40:32.413 に答える
4

バイナリIOの場合、私はstdioのfreadとfwriteを使用する傾向があります。フォーマットされたものの場合、私は通常IO Streamを使用しますが、Mikaelが言ったように、非自明な(デフォルトではない?)フォーマットはPITAになる可能性があります。

于 2008-09-23T04:56:40.067 に答える
4

C++ 標準ライブラリの 2 つの主流ライブラリを比較します。

C++ では、C スタイルのフォーマット文字列ベースの文字列処理ルーチンを使用しないでください。

それらの使用を制限するいくつかの理由があります。

  • タイプセーフではない
  • POD 以外の型を可変引数リストに渡すことはできません (つまり、scanf+co. にも printf+co. にも渡せません)。または、未定義の動作の暗い要塞に入ります。
  • 間違えやすい:
    • フォーマット文字列と「value-argument-list」の同期を維持する必要があります
    • 同期を正しく保つ必要があります

遠隔地で導入された微妙なバグ

良くないのはprintf自体だけではありません。ソフトウェアは古くなり、リファクタリングや変更が加えられ、遠隔地からエラーが発生する可能性があります。あなたが持っていると仮定します

.

// foo.h
...
float foo;
...

そしてどこか...

// bar/frob/42/icetea.cpp
...
scanf ("%f", &foo);
...

そして 3 年後、foo は何らかのカスタム型でなければならないことがわかりました...

// foo.h
...
FixedPoint foo;
...

しかし、どこか...

// bar/frob/42/icetea.cpp
...
scanf ("%f", &foo);
...

...その後、古いprintf / scanfは引き続きコンパイルされますが、ランダムなセグメンテーション違反が発生し、その理由を覚えていません。

iostream の冗長性

printf() の方が冗長ではないと思われる場合は、iostream を最大限に活用していない可能性があります。例:

  printf ("My Matrix: %f %f %f %f\n"
          "           %f %f %f %f\n"
          "           %f %f %f %f\n"
          "           %f %f %f %f\n",
          mat(0,0), mat(0,1), mat(0,2), mat(0,3), 
          mat(1,0), mat(1,1), mat(1,2), mat(1,3), 
          mat(2,0), mat(2,1), mat(2,2), mat(2,3), 
          mat(3,0), mat(3,1), mat(3,2), mat(3,3));

それを iostreams を正しく使用する場合と比較してください。

cout << mat << '\n';

おおよそ printf-thingy の構造を持つ operator<< の適切なオーバーロードを定義する必要がありますが、重要な違いは、再利用可能でタ​​イプセーフなものがあることです。もちろん、printf-likes で再利用可能なものを作成することもできますが、その場合、printf が再び使用されます (行列のメンバーを新しいものに置き換えたらどうなるFixedPointでしょうか?)。 .

C スタイルのフォーマット文字列は I18N には iostream よりも適していません

フォーマット文字列は国際化の助けになると考えられることが多いですが、その点では iostream よりも優れているわけではありません。

printf ("Guten Morgen, Sie sind %f Meter groß und haben %d Kinder", 
        someFloat, someInt);

printf ("Good morning, you have %d children and your height is %f meters",
        someFloat, someInt); // Note: Position changed.

// ^^ not the best example, but different languages have generally different
//    order of "variables"

つまり、古いスタイルの C フォーマット文字列には、iostream と同様に位置情報がありません。

boost::formatを検討することをお勧めします。これは、フォーマット文字列内の位置を明示的に指定するためのサポートを提供します。彼らの例のセクションから:

cout << format("%1% %2% %3% %2% %1% \n") % "11" % "22" % "333"; // 'simple' style.

一部の printf 実装は位置引数を提供しますが、それらは非標準です。

C スタイルの書式指定文字列を使用しないでください。

パフォーマンスを除けば (Jan Hudec が指摘したように)、理由がわかりません。ただし、次の点に注意してください。

「約 97% の確率で、わずかな効率性を忘れる必要があります。時期尚早の最適化はすべての悪の根源です。しかし、その重要な 3% の機会を逃してはなりません。優れたプログラマーは、そのような理由付けによって自己満足に陥ることはありません。彼は、重要なコードを慎重に検討するのが賢明です。ただし、そのコードが特定された後でのみ」 - Knuth

「ボトルネックは意外な場所で発生するので、そこがボトルネックであることが証明されるまで、推測してスピードハックを仕掛けようとしないでください。」-パイク

はい、printf-implementations は通常、iostreams よりも高速で、boost::format よりも高速です (私が書いた小さくて特定のベンチマークからですが、特に状況に大きく依存するはずです: printf=100% の場合、iostream=160% の場合) 、boost::format=220%)

しかし、やみくもに考えることを忘れないでください。テキスト処理に実際にどれくらいの時間を費やしていますか? プログラムは、終了するまでどのくらい実行されますか? C スタイルのフォーマット文字列にフォールバックしたり、型の安全性を緩めたり、リファクタリングの可能性を減らしたり、何年にもわたって隠れていたり、お気に入りの顧客が直面しているときにしか明らかにならない非常に微妙なバグの可能性を高めたりすることは、まったく関係がありますか?

個人的には、20% 以上のスピードアップが得られない場合は後退しません。しかし、私のアプリケーションは事実上すべての時間を文字列処理以外のタスクに費やしているため、そうする必要はありませんでした。私が作成したパーサーの中には、事実上すべての時間を文字列処理に費やしているものもありますが、それらの合計実行時間は非常に短いため、テストや検証に費やす価値はありません。

なぞなぞ

最後に、いくつかのなぞなぞをプリセットしたいと思います。

コンパイラはそうしないので、すべてのエラーを見つけます (彼は親切な場合にのみ提案できます)。

shared_ptr<float> f(new float);
fscanf (stdout, "%u %s %f", f)

他に何もない場合、これの何が問題になっていますか?

const char *output = "in total, the thing is 50%"
                     "feature  complete";
printf (output);
于 2011-11-24T11:12:43.953 に答える
3

C++ iostreams API には多くの利点がありますが、i18n に関する重大な問題が 1 つあります。問題は、パラメーター置換の順序がカルチャによって異なる可能性があることです。古典的な例は次のようなものです。

// i18n UNSAFE 
std::cout << "Dear " << name.given << ' ' << name.family << std::endl;

これは英語では機能しますが、中国語では姓が最初に来ます。

海外市場向けにコードを翻訳する場合、スニペットの翻訳には危険が伴うため、新しい l10ns では文字列の変更だけでなく、コードの変更が必要になる場合があります。

boost::format は、stdio (パラメーターを異なる順序で使用できる単一のフォーマット文字列) と iostream (型安全性、拡張性) の長所を組み合わせているようです。

于 2009-11-23T07:26:18.543 に答える
2

私はiostreamを使用します。これは主に、後でストリームをいじるのが簡単になるためです(必要な場合)。たとえば、出力をトレースウィンドウに表示したい場合があります。これは、coutとcerrを使用すると比較的簡単に実行できます。もちろん、UNIXでパイプなどをいじることはできますが、それは移植性がありません。

私はprintfのようなフォーマットが大好きなので、通常は最初に文字列をフォーマットしてから、それをバッファーに送信します。Qtでは、QString :: sprintfをよく使用します(代わりにQString :: argを使用することをお勧めします)。boost.formatも調べましたが、構文に実際に慣れることができませんでした(%が多すぎます)。でも、実際に見てみる必要があります。

于 2008-09-23T06:22:14.997 に答える
2

ilibraries について私が見逃しているのは、フォーマットされた入力です。

iostreams には scanf() を複製する適切な方法がなく、boost でさえ入力に必要な拡張子がありません。

于 2008-12-13T19:37:06.897 に答える
1

iostream は標準になっているため、コードが新しいバージョンのコンパイラで確実に動作することを認識して使用する必要があります。最近では、ほとんどのコンパイラが iostream についてよく知っているので、それらを使用しても問題はないはずです。

しかし、*printf 関数に固執したい場合、私の意見では問題はありません。

于 2008-09-23T07:18:09.917 に答える
1

stdio は、バイナリ ファイルの読み取りに適しています (ブロックを vector<unsigned char> に freading したり、.resize() を使用したりするなど)。例については、 http: //nuwen.net/libnuwen.html の file.hh の read_rest 関数を参照してください。

C++ ストリームは、バイナリ ファイルを読み取るときに大量のバイトを詰まらせ、誤った eof を引き起こす可能性があります。

于 2008-09-23T06:01:51.410 に答える