printf和std::cout的区别

17

可能是重复问题:
我应该在我的C++代码中使用printf吗?

如果我只想在屏幕上打印一个字符串,我可以使用以下两种方式:

printf("abc");

std::cout << "abc" << std::endl;

那么,在上面展示的例子中,使用printf优于std::cout还是反过来更有优势呢?


7
如果你在写C++代码,通常应该优先选择使用C++特有的习惯用法和库。 - Paul R
这取决于你是用C还是C++编写程序。这两种方法属于不同的语言,因此不能直接进行比较。 - Harry Joy
1
@Paul R. 感谢您的回复。那么,“printf()”不被认为是C++习语吗? - aali
@Harry Joy。感谢您的回复。我正在使用C++。 - aali
这是一个自由函数,C++支持自由函数,并且标准库中有自由函数。非C++惯用语可能是省略号(...),通常只在C++中用作SFINAE的“作弊”。 - CashCow
3
我应该在我的C代码中使用printf吗? - mingos
7个回答

37

虽然 cout 是C++的正确方式,但我认为一些人和公司(包括 Google)继续在C++代码中使用printf,因为使用printf比使用cout更容易进行格式化输出。

下面是我在这里找到的一个有趣的例子。

对比:

printf( "%-20s %-20s %5s\n" , "Name" , "Surname" , "Id" );
并且
cout << setw( -20 ) << "Name" << setw( 20 ) << "Surname"  << setw( 5 ) << "Id" << endl;

5
+1,你已经阐述了printf的一个优点,看起来对我来说是一个有效的答案。 - CashCow
7
代码长度较短并不一定意味着更易读,下面是一个例子:std::cout版本清晰地展示了每个步骤正在执行的操作。相比之下,printf则使用了难以理解的格式化字符串。 - Zac Howland
3
没问题,但一旦你习惯了它们,就可以说printf比两者更易于使用。个人而言,我总觉得流格式化很笨重。 - dandan78
1
-1:正确使用 cout 的方式是:cout << person << '\n'。这是一个常见的误解。 - Sebastian Mach
怎么样?endl = 结束行!? - paulm
2
endl 应该只在想要刷新流时使用。 - Cerran

19

printf及其相关函数是C语言的函数。它们在C++中可以工作,但不具有C++ std::ostream的类型安全性。使用printf函数基于用户输入(甚至是来自文件的输入)格式化输出的程序可能会出现问题。例如:

int main()
{
    char[] a = {'1', '2', '3', '4'}; // a string that isn't 0-terminated
    int i = 50;
    printf("%s", a); // will continue printing characters until a 0 is found in memory
    printf("%s", i); // will attempt to print a string, but this is actually an integer
}

C++具有更强的类型安全性,还有一个std::string类,可以帮助防止这些问题。


1
@Zac Howland。感谢您的回复。为什么您在上面声明的字符串没有以0结尾?难道字符串不是总是以0结尾吗?另外,您所说的“类型安全”是什么意思?谢谢。 - aali
3
他定义了一个字符数组,这些字符数组在C语言中可以被解释为字符串,但它不会自动在数组末尾添加一个零字节。 - Ward Muylaert
@Ward Muylaert。当我们在C++中像这样声明字符串时,难道不会自动添加\0来表示序列的结尾吗?或者说,在什么情况下会得到\0呢?谢谢。 - aali
3
不会自动添加终止符号。如果我声明为 char* a = "1234";,那么就会有一个终止符号。但是,当你没有终止符号时,我正在演示的问题就会出现。 - Zac Howland
3
@Zac,我认为@CashCow试图表达的是,在这种情况下两种情况完全相同。如果你限制自己只使用 std::string而不是 const char *,那么你不会在使用cout时遇到问题,但在使用printf( "%s\n", st.c_str() );时也不会有问题。我同意你的答案,但类型安全问题随着时间的推移而变得模糊了...... gcc将通过解析调用处的格式字符串来验证类型正确性,并提供编译器警告。这不是标准要求的,但它确实存在。 - David Rodríguez - dribeas
显示剩余5条评论

3
我本人也在思考这个问题。printf通常更容易用于格式化打印,但C++中的iostreams工具有一个很大的优点,即您可以为对象创建自定义格式化程序。我最终会根据需要在代码中使用它们两个。
同时使用并混合它们的问题在于,printf和cout使用的输出缓冲区不同,因此,除非您运行未缓冲或显式刷新输出,否则可能会出现损坏的输出。
我对C ++的主要反对意见是,没有类似于printf的快速输出格式化工具,因此没有办法轻松地控制整数、十六进制和浮点格式化的输出。
Java也面临了同样的问题;该语言最终获得了printf。
维基百科在http://en.wikipedia.org/wiki/Printf#C.2B.2B_alternatives_to_sprintf_for_numeric_conversion上对这个问题进行了很好的讨论。

2
实际上,对于您的具体示例,您应该问哪个更可取,puts还是cout。printf打印格式化文本,但您只是向控制台输出普通文本。
对于一般用途,流(iostream,其中包括cout)更具可扩展性(您可以使用它们打印自己的类型),并且更通用,因为您可以生成函数以打印任何类型的流,而不仅仅是控制台(或重定向输出)。您也可以使用fprintf创建基本流行为,其使用FILE *作为FILE *通常不是真正的文件,但这更加棘手。
流是“类型安全”的,因为您会重载正在打印的类型。 printf并非使用省略号是类型安全的,因此,如果放置不匹配格式字符串的错误参数类型,则可能会获得未定义的结果,但编译器不会发出警告。如果您错过了参数或传入错误的参数(例如将数字用于%s,它仍将将其视为指针),则甚至可能会获得seg-fault /未定义行为(但如果不正确使用cout也会发生这种情况)。 printf确实具有一些优点:您可以模板化格式字符串,然后重用该格式字符串以处理不同的数据,即使这些数据不在结构中,并且对一个变量使用格式化操作不会使该格式“粘滞”以供进一步使用,因为您为每个变量指定格式。 printf也被认为是线程安全的,而cout实际上并不安全。
boost使用其boost :: format库结合了每个库的优点。

+1 提到 boost::format - GrahamS
“+1”:“你应该问哪个更好,puts 还是 cout”。我正在搜索以尝试找出谁在内部调用谁,puts() 中是否有一个 cout 调用? - LoneXcoder

2
printf是从C语言中借用的,有一些局限性。最常见的限制是类型安全性,因为它依赖于程序员正确匹配格式字符串和参数。第二个限制来自于可变参数环境,即无法使用用户定义的类型扩展行为。printf知道如何打印一组类型,这就是你可以得到的全部内容。尽管如此,对于可以使用printf处理的少数事情而言,与c++流相比,使用printf更快且更简单。
虽然大多数现代编译器能够解决类型安全限制,并至少提供警告(编译器可以解析格式字符串并检查调用中提供的参数),但第二个限制无法克服。即使在第一种情况下,编译器也不能真正帮助解决一些问题,例如检查空终止符--但同样的问题也会出现在如果使用std::cout打印相同数组的情况下。
另一方面,通过重载给定用户定义类型typestd::ostream& operator<<( std::ostream&, type const & ),流(包括std::cout)可以扩展以处理用户定义类型。它们本身是类型安全的——如果传入一个没有重载operator<<的类型,编译器将会报错。然而,它们更加繁琐以产生格式化输出。
那么你应该使用哪个呢?一般来说,我更喜欢使用流,因为为我的自定义类型重载operator<<很简单,并且可以统一地与所有类型一起使用。

1

这两个例子做的事情不同。后者会添加一个换行符并刷新输出(std::endl 的结果)。std::cout 也比较慢。除此之外,printfstd::cout 实现了相同的功能,你可以选择任何一个。就个人而言,在 C++ 代码中我会使用 std::cout。它更易读且更安全。

如果您需要使用 std::cout 格式化输出,请参阅this article


谢谢您的回复。您提到的为什么“std::cout”会更慢?这里的“更慢”是什么意思?例如在哪方面更慢?谢谢。 - aali
@aali,其他答案已经详细解释了原因,所以它的速度较慢:它要做更多的事情。它非常依赖于你的编译器,但我曾经看到过旧版本的GCC在所有情况下都比现在慢10倍以上。 - moinudin
使用g++4.x编译器,在我提交的测试用例中,如果你使用sync_with_stdio(false),那么输出流与printf函数一样快。 - Sebastian Mach

0
通常而言,你应该优先选择cout,因为它更加类型安全和通用。printf没有类型安全性,也不通用。唯一的原因是你可能会更倾向于printf是因为它速度很快-从内存中来看,printf比cout快得多。

谢谢您的回复。我看到“printf()”可以进行格式化,那么“cout”也提供这个功能吗? - aali
当你说“printf既不是类型安全的,也不是通用的。你可能偏爱printf的唯一原因是速度-从记忆中来看,printf比cout快得多。”时,你的意思是什么?你能再解释一下吗?谢谢。 - aali
@aali:cout 还提供了输出格式化。 - Harry Joy
你有printf速度快多少倍的参考资料吗? - CB Bailey
@aali:你的编译器会接受 printf 中的明显错误,比如 printf("%s", int());。此外,许多自定义类型被重载以与 iostreams 一起使用,但你永远无法使用 printf 打印自定义类型。cout 也提供格式化功能。@Charles:我个人没有。我知道 iostream 以非常慢的速度运行而闻名,当我测试它时,与 printf 相比,它非常慢。 - Puppy
@DeadMG:只有在与默认启用的stdio同步时,printf才更快。 - Sebastian Mach

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接