たとえ質問が違いを求めたとしても、この質問の誰もがstd::cout
がprintf
よりずっと優れていると主張しているのは驚きです。今は違いがあります - std::cout
はC++、そしてprintf
はCです(ただし、Cの他ののように、C++でも使用できます)。さて、私はここで正直に言うでしょう。 printf
とstd::cout
の両方に利点があります。
std::cout
は拡張可能です。私は人々がprintf
も拡張可能であると言うことを知っていますが、そのような拡張はC標準では言及されていません(だから非標準的な機能を使う必要があるでしょう。レター(つまり、既存のフォーマットと競合するのは簡単です)。
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
フォーマット文字列は、文字列です。 iostream
のoperator <<
の悪用と比較して、それは翻訳を本当に簡単にします。 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 .
\0
printf
はC++文字列とは対照的にC文字列を使用するため、特別なトリックなしにNULバイトを表示することはできません。場合によっては、%c
を引数として'\0'
を使用することは可能ですが、これは明らかにハックです。
更新日:iostream
は非常に遅いので、通常はハードドライブより遅くなります(プログラムをファイルにリダイレクトする場合)。大量のデータを出力する必要がある場合は、stdio
との同期を無効にすると効果的です。 (STDOUTに複数の行を書き込むのではなく)パフォーマンスが実際に問題になる場合は、単にprintf
を使用してください。
誰もが彼らがパフォーマンスを気にすると考えていますが、誰もそれを測定しようとは思わない。私の答えは、printf
とiostream
のどちらを使用しても、とにかく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);
^
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::ostream
やstd::istream
などの実際のクラスから構築されています。<cstdio>
のFILE*
とは異なり、これらは実際のクラスなので継承可能です。これは、ストリームのように見え、動作する他のユーザー定義のものを持つことができるということを意味します。あなたは、あなたも知らないユーザによって書かれた無数のI/Oコードを自動的に使うようになります。そして、彼らはあなたの "拡張ストリーム"クラスについて知る必要はありません。
一方、printf
はかなり高速であるため、very特定の限られたケースでは、cout
より優先して使用するのが妥当な場合があります。常に最初にプロファイルします。 (例えば、 http://programming-designs.com/2009/02/c-speed-test-part-2-printf-vs-cout /]を参照してください)
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が常に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);
}
}
そして私は 引用 :
大まかに言えば、主な違いは型の安全性(cstdioにはない)、パフォーマンス(ほとんどの入出力ストリームの実装はcstdioの実装より遅い)、および拡張性(入出力ストリームはカスタム出力ターゲットとユーザー定義型のシームレス出力を可能にする)です。
一つは標準出力に出力する関数です。もう1つは、標準出力に出力されるいくつかのメンバ関数とoperator<<
のオーバーロードを提供するオブジェクトです。私が列挙できる他の多くの違いがありますが、私はあなたが何をしているのかよくわかりません。
私にとっては、私が '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
内のデータのフォーマット(精度、位置揃えなど)はより簡単です。
ここで特に言及されていない2つの点は、私が重要だと思います。
1)STLをまだ使用していない場合、cout
は多くの荷物を持っています。それはあなたのオブジェクトファイルにprintf
の2倍以上のコードを追加します。これは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"を追加したとしましょう。痛風で、あなたは一度だけそれを変更する必要があります。しかし、printfを使用すると、それを変更する必要がある可能性があります。それだけでなく、どの出力を出力するのかを自覚する必要があります。
そうは言っても、coutを使えば、コードのメンテナンスに費やす時間を大幅に減らすことができます。それだけでなく、新しいアプリケーションでオブジェクト「Something」を再利用しても、出力について心配する必要はありません。
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
楽しむ!
もちろんメンテナンスを維持するためにもう少し「何か」を書くことができます。
#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
その他の違い: "printf"は整数値(印刷された文字数に等しい)を返し、 "cout"は何も返しません。
そして。
cout << "y = " << 7;
はアトミックではありません。
printf("%s = %d", "y", 7);
はアトミックです。
coutは型検査を行いますが、printfは行いません。
"% d"
と同等の入出力ストリームはありません。
私はprintf
の拡張性の欠如が完全に真実ではないと言いたいです:
Cでは、その通りです。しかし、Cでは、実際のクラスはありません。
C++では、キャスト演算子をオーバーロードすることが可能であるため、char*
演算子をオーバーロードしてprintf
を次のように使用します。
Foo bar;
...;
printf("%s",bar);
fooが良い演算子をオーバーロードするなら、それは可能です。またはあなたが良い方法を作った場合。要するに、私にとってはprintf
はcout
と同じくらい拡張可能です。
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()
と書きますが、書き方は?)
私はプログラマーではありませんが、私はヒューマンファクターエンジニアです。プログラミング言語は習得が容易で理解しやすいものであるべきだと私は感じます、そしてこれはそれが単純で一貫した言語構造を持つことを必要とします。すべての言語は象徴的であり、したがって、その核心では任意ですが、規約があり、それに従うことで言語の習得と使用が容易になります。
C++やその他の言語では、関数(パラメータ)として書かれた膨大な数の関数があります。これは、コンピュータ以前の時代の数学の関数関係に元々使用されていた構文です。 printf()
はこの構文に従います。もしC++の作者がファイルを読み書きするための論理的に異なる方法を作りたいのであれば、彼らは同じような構文を使って異なる関数を単に作成したかもしれません。
Pythonでは、変数はオブジェクトなので、もちろん標準的なobject.method
構文、すなわちvariablename.printを使って印刷することもできますが、C++ではそうではありません。
<<演算子は規則に従わないので、私はcout構文が好きではありません。それは方法または機能であり、すなわちそれはパラメータを取りそしてそれに対して何かをする。しかし、それはあたかもそれが数学的比較演算子であるかのように書かれています。これは人的要因の観点からは不適切なアプローチです。
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。
cout<< "Hello";
printf("%s", "Hello");
両方とも値を印刷するために使用されます。それらは完全に異なる構文を持っています。 C++には両方があり、Cにはprintfしかありません。