我应该在我的C ++代码中使用printf吗?

我通常使用coutcerr将文本写入控制台。 不过有时我发现使用旧的printf语句更容易。 我需要格式化输出时使用它。

我将使用这个的一个例子是:

 // 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语句吗?

我的学生先学习cincout ,然后学习printf ,压倒性地喜欢printf (或者更通常的fprintf )。 我自己发现printf模型足够可读,我已经将它移植到其他编程语言。 Olivier Danvy也是如此 ,他甚至把它变成了types安全的。

假如你有一个编译器能够检查对printf调用,我没有理由不使用fprintf和C ++中的朋友。

免责声明:我是一个可怕的C ++程序员。

如果你曾经希望在你的程序中,远离iostreams。 问题是,如果句子是由多个片段组成的,就像使用iostream所做的那样,就不可能正确地定位你的string。

除了消息片段的问题之外,还有一个命令的问题。 考虑一个打印学生姓名和平均成绩的报告:

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

当你翻译成另一种语言时,另一种语言的语法可能需要你在名称前面显示GPA。 AFAIK,iostreams没有办法对内插值重新sorting。

如果你想两全其美(types安全,能够国际化),使用Boost.Format 。

使用boost :: format。 你得到types安全,std :: string支持,printf类似的界面,使用cout的能力,以及其他很多好东西。 你不会回去的。

适应性

任何尝试printf非POD都会导致未定义的行为:

 struct Foo { virtual ~Foo() {} operator float() const { return 0.f; } }; printf ("%f", Foo()); std::string foo; printf ("%s", foo); 

上面的printf调用产生未定义的行为。 您的编译器可能会警告您,但这些警告不是标准所要求的,对于只在运行时才知道的格式string是不可能的。

IOstream:

 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; } 

C:

 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); 

C ++:

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

自我评判

国际化

我们重用我们的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()); 

自我评判

性能

  1. 你是否测量了printf性能的实际意义? 你的瓶颈应用程序是否严重懒惰,计算结果的输出是一个瓶颈? 你确定你需要C ++吗?
  2. 可怕的性能损失是为了满足那些想要使用printf和cout的用户。 这是一个function,而不是一个错误!

如果你一直使用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。

您可以在C ++ 11中使用types安全的,I18N友好的printf,这要归功于可变参数模板。 而且你将能够使用用户定义的文字非常,非常高效地执行它们,也就是说可以写出一个完全静态的化身。

我有一个概念的certificate 。 那时候,对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中犯了很多错误,特别是当你把用户inputstring混合在一起(想想你的I18N团队)。 您必须小心妥善地转义每一个这样的格式string,您必须确保传递正确的参数等。

  • IO-Streams使我的二进制变大 :如果这是比可维护性,代码质量,可重用性更重要的问题,那么(在validation问题之后!)使用printf。

我使用printf是因为我讨厌丑陋的<<cout<<语法。

没有理由。 我认为这只是一些奇怪的意识形态,驱使人们只使用C ++库,即使旧的C库仍然有效。 我是一个C ++的人,我也使用C函数。 从来没有与他们有任何问题。

stream是规范的方式。 尝试使这个代码与printf工作:

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

祝你好运。

我的意思是说,你可以让运营商让你的types输出到ostream的,而不用像任何其他types的麻烦。 printf不符合C ++的一般性,或更具体的模板。

有更多的可用性。 还有一致性。 在我所有的项目中,我都有cout(以及cerrclog )也可以输出到一个文件中。 如果您使用printf ,则跳过所有这些。 另外,一致性本身是一件好事, 混合coutprintf ,而完全有效,是丑陋的。

如果你有一个对象,并且想要使其成为可输出的,那么最简洁的方法就是为该类重载operator<< 。 你打算如何使用printf呢? 你最终会遇到与coutprintf混杂在一起的代码。

如果您确实想要格式化,请在维护stream接口的同时使用Boost.Format。 一致性格式。

使用printf。 不要使用C ++stream。 printf给你更好的控制(如浮点精度等)。 代码通常也更短,更易读。

谷歌C + +风格指南同意。

不要使用stream,除非日志logging界面需要。 改用printf-like例程。

使用stream的方式有各种各样的优点和缺点,但在这种情况下,和许多其他情况一样,一致性胜过了辩论。 不要在代码中使用stream。

总的来说,我同意(憎恨<<语法,特别是如果你需要复杂的格式)

但是我应该指出安全问题。

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

可能不会被编译器注意到,但可能会导致应用程序崩溃。

使用任何适合您的需求和偏好。 如果你对printf感到满意,那么一定要用它。 如果你喜欢iostreams坚持他们。 根据您的要求混合搭配。 毕竟,这是软件 – 有更好的方法和更糟糕的方法,但很less有只有一种方法。

分享和享受。

我不喜欢printf。 它缺乏types安全性,使用起来很危险,加上需要记住格式说明符是一个痛苦。 聪明地做正确事情的模板操作员要好得多。 所以我总是在C ++中使用C ++stream。

当然,许多人更喜欢printf, 其他原因,在其他地方列举。

我经常“回退”使用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, ...); 

更新

在阅读了其他一些答案之后,我可能不得不做一个switch boost::format()我自己!

我几乎总是使用printf作为临时debugging语句。 对于更长久的代码,我更喜欢'C'stream,因为它们是C ++的方式 。 虽然boost :: format看起来很有前景,可能会替代我的stream的使用(特别是对于格式复杂的输出),但很可能没有什么东西会替代printf。

C ++ stream被高估了,毕竟它们实际上只是带有重载运算符<<
我已经阅读过很多次, C ++的方式是printf,但是C ++都提供了库函数,所以你应该使用最适合的方法。
我主要更喜欢printf,但我也使用了stream,它提供了更清晰的代码,并防止您必须将%占位符与参数匹配。

这取决于实际情况。 没有什么是完美的。 我用两个。 stream对于自定义types是很好的,因为你可以在ostream中重载>>操作符。 但是当涉及到间距等,最好使用printf()。 stringstream和like样式都比C风格的strcat()好。 所以用一个适合的情况。

即使这个问题比较老,我想补充两分钱。

用printf()打印用户创build的对象

这很简单,如果你考虑一下 – 你可以将你的typesstring化,并把string发送到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()陷阱

一个标志 – %n

唯一一个是输出参数 – 它指望int的指针。 它将成功写入的字符数写入该指针指向的位置。 技巧性的使用会触发溢出,这是安全漏洞(参见printf()格式的string攻击)。

我读过警告说cout和cerr对于multithreading是不安全的。 如果属实,这是避免使用它们的一个很好的理由。 注意:我使用openMP的GNU g ++。

溪stream在cpp中是优选的,因为它们遵循cpp的面向对象的范例,除了types安全之外。

另一方面,printf更多的是一种function性的方法。

唯一的理由不使用printf在cpp代码,我能想到的不是面向对象。

更多的是个人select。