回答:
この質問のすべての人が、質問が違いを尋ねただけでstd::cout
もprintf
、それよりもはるかに優れていると主張していることに驚いています。今、違いstd::cout
があります-C ++であり、printf
Cです(ただし、Cの他のほとんどすべてと同様に、C ++ でも使用できます)。さて、私はここで正直になります。両方printf
とstd::cout
その利点があります。
std::cout
拡張可能です。私は人々がそれprintf
も拡張可能であると言うだろうことを知っていますが、そのような拡張はC標準では言及されていません(そのため、非標準機能を使用する必要があります-しかし、一般的な非標準機能さえ存在しません)、そのような拡張は1文字です(したがって、既存のフォーマットと競合するのは簡単です)。
とは異なりprintf
、std::cout
演算子のオーバーロードに完全に依存しているため、カスタム形式の問題はありませんstd::ostream
。最初の引数として、2番目の型としてサブルーチンを定義するだけです。したがって、名前空間の問題はありません。クラス(1文字に限定されない)がある限り、そのクラスのstd::ostream
オーバーロードを機能させることができます。
しかし、多くの人が拡張したいと思うのではないかと思いますostream
(正直なところ、簡単に作成できたとしても、そのような拡張機能はめったに見ませんでした)。ただし、必要な場合はここにあります。
簡単にわかるように、printf
とstd::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
フォーマット文字列は...よく文字列です。これにより、の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
、整数型に応じて特定の構文が必要です(非整数型もありますが、実際に使用する唯一の非整数型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で入手できます。
\0
printf
はC ++文字列ではなくC文字列を使用するため、特別なトリックがないとNULバイトを出力できません。特定の場合にはそれを使用することが可能です%c
と'\0'
それは明らかにハックですが、引数として。
更新:それiostream
は非常に遅いので、ハードドライブよりも遅いことがわかります(プログラムをファイルにリダイレクトする場合)。stdio
大量のデータを出力する必要がある場合は、との同期を無効にすると役立つ場合があります。(STDOUTに複数の行を書き込むのではなく)パフォーマンスが実際の問題である場合は、を使用しますprintf
。
誰もがパフォーマンスを気にかけていると思っていますが、誰もそれを測定する気になりません。とにかく、printf
またはを使用しても、I / Oがボトルネックになるというのが私の答えですiostream
。私は考えるprintf
ことができ(使用して打ち鳴らすしてコンパイルアセンブリに簡単に見てから速くなる-O3
コンパイラオプションを)。私のエラー例を想定すると、例はprintf
例よりもはるかに少ない呼び出しを行いcout
ます。これはあるint main
とprintf
:
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);
^
std::sort
、を除いて、qsort
(2回)に比べて驚くほど高速です)実行可能サイズのコスト)。
[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 /を参照)
printf()
拡張可能であることになっています。udrepper.livejournal.com/20948.htmlにある
printf
はそのような機能はありません。移植性のないライブラリメカニズムは、iostreamの完全に標準化された拡張性と同じレベルにはほとんどありません。
人々はしばしばそれ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
ほぼ同じくらい高速です。詳細については、私のブログをご覧ください。
明確にするために、私はiostream
sが常に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);
}
}
printf()
とは、std::ostream
ということである単一のコール内のすべての引数が前者の出力に対し、std::ostream
それぞれに別々の呼び出しを招きます<<
。テストは1つの引数と改行のみを出力するため、違いがわかりません。
printf
さまざまな書式指定子のヘルパー関数を隠蔽して多くの呼び出しを行う可能性があります...または、それは巨大なモノリシック関数です。繰り返しになりますが、インライン化のため、速度にまったく違いがありません。
sprintf
またはfprintf
およびstringstream
またはを使用しfstream
ます。
そして私は引用します:
大まかに言えば、主な違いは型の安全性(cstdioにはない)、パフォーマンス(ほとんどのiostreamの実装はcstdioの実装よりも遅い)、および拡張性(iostreamはカスタム出力ターゲットとユーザー定義型のシームレスな出力を可能にする)です。
私にとって、「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
方が簡単です。
printf
て、それを交換することにより、同様のファイルにfprintf
...
ここでは特に触れていないが、私が重要だと思う2つの点:
1)cout
まだSTLを使用していない場合、手荷物が多くなります。オブジェクトファイルに2倍以上のコードを追加しますprintf
。これは、string
、これが私が自分の文字列ライブラリを使用する主な理由です。
2)cout
オーバーロードされた<<
演算子を使用していますが、これは残念です。これを使用している場合、混乱が生じる可能性があります。<<
演算子を意図した目的(左シフト)に。個人的には、意図された用途に正接する目的でオペレーターに負荷をかけたくない。
結論:すでにSTLを使用している場合はcout
(およびstring
)を使用します。そうでなければ、私はそれを避ける傾向があります。
プリミティブを使用する場合は、どれを使用するかはまったく問題ではありません。便利なのは、複雑なオブジェクトを出力する場合です。
たとえば、クラスがある場合、
#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」オブジェクトを再利用する場合は、出力について心配する必要はありません。
もちろん、メンテナンスを維持するために「何か」を少し上手に書くことができます。
#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'
ないのですか?
endl
バッファをフラッシュするためであり、フラッシュ\n
しないためだと思いますが、これが明確な理由であるかどうかはわかりません。
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
楽しんで!
thread
は出力を大げさにしません。私は再現して、両方xyz
とABC
出力で見つけました。そこは、W / BをマングリングされなかったABC
としてABABAB
。
cout
スレッドの処理方法はわかりませんが、表示しているコードが、これらの出力を取得するために使用したコードではないことは確かです。あなたのコードでは、文字列を渡し"ABC"
、スレッド1用と"xyz"
、スレッド2のために、しかし、あなたの出力が示すAAA
とBBB
。混乱しているので修正してください。
cout<< "Hello";
printf("%s", "Hello");
どちらも値の印刷に使用されます。構文はまったく異なります。C ++には両方があり、Cにはprintfしかありません。
拡張性の欠如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()
私たちが読みたい場合は、しかし、どのように書き込みますか?)
char*
は使用されません。
char*
あり、どこにどのくらいの期間、ユーザー定義の危険性ます暗黙のキャスト。
その他の違い:「printf」は整数値(印刷された文字数に等しい)を返し、「cout」は何も返しません
そして。
cout << "y = " << 7;
アトミックではありません。
printf("%s = %d", "y", 7);
アトミックです。
coutはタイプチェックを実行しますが、printfは実行しません。
同等のiostreamはありません "% d"
cout
これは関数ではなくオブジェクトであるため、何も返しません。operator<<
何かを返します(通常は左側のオペランドですが、エラーがある場合はfalse値)。そして、どのような意味でprintf
「アトミック」という呼び方ですか?
printf("%s\n",7);
%s
ですか?
printf
%sの引数がnullで終わる文字列への有効なポインタを持っている必要があります。通常、メモリ範囲「7」(ポインタ)は無効です。セグメンテーション違反は幸運かもしれません。一部のシステムでは、「7」がコンソールに大量のゴミを出力することがあり、プログラムが停止する前に1日間それを確認する必要があります。つまり、これはの悪い点ですprintf
。静的分析ツールは、これらの問題の多くを捉えることができます。
printf
は型チェックを行いませんが、型エラーについて警告しないコンパイラを使用したことはありませんprintf
...
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 ++やその他の言語には、関数(パラメーター)として書かれた膨大な数の関数があります。 printf()
この構文に従い、C ++の作成者がファイルを読み書きするための論理的に異なるメソッドを作成する場合は、同様の構文を使用して異なる関数を作成するだけで済みます。
Pythonではもちろんobject.method
、変数もオブジェクトなので、かなり標準的な構文、つまりvariablename.print を使用して印刷できますが、C ++ではそうではありません。
<<演算子はルールに従っていないため、cout構文は好きではありません。これはメソッドまたは関数です。つまり、パラメーターを取り、それに何かを行います。ただし、数学的な比較演算子のように書かれています。これは、人的要因の観点からは不十分なアプローチです。