C ++中的’printf’与’cout’

您所在的位置:网站首页 printf比cout快多少 C ++中的’printf’与’cout’

C ++中的’printf’与’cout’

2023-05-17 00:38| 来源: 网络整理| 查看: 265

C ++中printf()和cout有什么区别?

我很惊讶这个问题中的每个人都声称std::cout比printf更好,即使这个问题只是要求差异。现在,有一个区别 - std::cout是C ++,printf是C(但是,您可以在C ++中使用它,就像C中的其他任何东西一样)。现在,我会在这里说实话; printf和std::cout都有其优点。

真正的差异

可扩展性

std::cout是可扩展的。我知道人们会说printf也是可扩展的,但是在C标准中没有提到这样的扩展(所以你必须使用非标准功能 - 但是甚至不存在常见的非标准功能),以及这样的扩展是一个字母(所以很容易与已经存在的格式冲突)。

与printf不同,std::cout完全取决于运算符重载,因此自定义格式没有问题 - 您所要做的就是定义一个子程序,将std::ostream作为第一个参数,将您的类型作为第二个参数。因此,没有命名空间问题 - 只要你有一个类(不限于一个字符),你可以为它工作std::ostream重载。

但是,我怀疑很多人会想要扩展ostream(说实话,我很少看到这样的扩展,即使它们很容易制作)。但是,如果你需要它就在这里。

句法

由于很容易注意到,printf和std::cout都使用不同的语法。 printf使用模式字符串和可变长度参数列表使用标准函数语法。实际上,printf是C拥有它们的原因 - printf格式过于复杂,没有它们就无法使用。但是,std::cout使用不同的API - 返回自身的operator ;lt;;lt; API。

通常,这意味着C版本会更短,但在大多数情况下它并不重要。打印多个参数时,差异很明显。如果您必须编写类似Error 2: File not found.的内容,假设错误编号,并且其描述是占位符,则代码将如下所示。两个示例的工作方式相同(好吧,实际上,std::endl刷新缓冲区)。

123printf("Error %d: %s. ", id, errors[id]); std::cout ;lt;;lt;"Error" ;lt;;lt; id ;lt;;lt;":" ;lt;;lt; errors[id] ;lt;;lt;"." ;lt;;lt; std::endl;

虽然这看起来并不太疯狂(只是它的两倍),但实际设置参数格式时,事情变得更加疯狂,而不仅仅是打印它们。例如,打印像0x0424这样的东西真是太疯狂了。这是由std::cout混合状态和实际值引起的。我从未见过类似std::setfill之类的语言(当然不是C ++)。 printf清楚地分隔了参数和实际类型。我真的更愿意维护它的printf版本(即使它看起来有点神秘)与iostream版本相比(因为它包含太多噪音)。

123printf("0x%04x ", 0x424); std::cout ;lt;;lt;"0x" ;lt;;lt; std::hex ;lt;;lt; std::setfill('0') ;lt;;lt; std::setw(4) ;lt;;lt; 0x424 ;lt;;lt; std::endl;

翻译

这就是printf的真正优势所在。 printf格式字符串很好......一个字符串。与operator ;lt;;lt;滥用iostream相比,这使得翻译变得非常容易。假设gettext()函数转换,并且您想要显示Error 2: File not found.,那么用于转换先前显示的格式字符串的代码将如下所示:

12printf(gettext("Error %d: %s. "), id, errors[id]);

现在,让我们假设我们翻译为Fictionish,其中错误编号在描述之后。翻译后的字符串看起来像%2$s oru %1$d. 。现在,如何在C ++中完成它?好吧,我不知道。我想你可以伪造iostream构造printf,你可以传递给gettext或其他东西,以便进行翻译。当然,$不是C标准,但它很常见,在我看来它是安全的。

不必记住/查找特定的整数类型语法

C有很多整数类型,C ++也是如此。 std::cout为您处理所有类型,而printf需要特定语法,具体取决于整数类型(有非整数类型,但实际使用printf时唯一的非整数类型是const char *( C字符串,可以使用std::string的to_c方法获得)。例如,要打印size_t,您需要使用%zd,而int64_t需要使用%"PRId64"。这些表格可在http://en.cppreference.com/w/cpp/io/c/fprintf和http://en.cppreference.com/w/cpp/types/integer上找到。

您无法打印NUL字节\0

因为printf使用C字符串而不是C ++字符串,所以如果没有特定的技巧,它就无法打印NUL字节。在某些情况下,可以使用%c和'\0'作为参数,尽管这显然是一个黑客攻击。

无人问津的差异

性能

更新:事实证明iostream是如此之慢,以至于它通常比您的硬盘驱动器慢(如果您将程序重定向到文件)。如果需要输出大量数据,禁用与stdio的同步可能会有所帮助。如果性能是一个真正的问题(而不是向STDOUT写几行),只需使用printf。

每个人都认为他们关心表现,但没有人愿意测量它。我的答案是无论如何,无论你使用printf还是iostream,I / O都是瓶颈。我认为printf从快速查看汇编(使用-O3编译器选项使用clang编译)可能会更快。假设我的错误示??例,printf示例执行的调用少于cout示例。这是int main,printf:

123456789101112main:                                   @ @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(数字)被推送为printf参数。就是这样;没有别的。为了比较,这是iostream编译为汇编。不,没有内联;每个operator ;lt;;lt;调用意味着使用另一组参数进行另一次调用。

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263main:                                   @ @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实现使用计算goto实现printf格式,因此printf尽可能快,即使没有编译器知道printf(不是它们不是 - 一些编译器可以优化printf in 某些情况 - 以 结尾的常量字符串通常优化为puts。

遗产

我不知道你为什么要继承ostream,但我不在乎。 它也可以用FILE。

1class MyFile : public FILE {}

类型安全

确实,可变长度参数列表没有安全性,但这并不重要,因为如果启用警告,流行的C编译器可以检测printf格式字符串的问题。 事实上,Clang可以在不启用警告的情况下做到这一点。

123456789101112131415161718192021222324$ cat safety.c #include ;lt;stdio.h;gt; int main(void) {     printf("String: %s ", 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 ", 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 ", 42);      ^

好。

相关讨论 你说I / O无论如何都是瓶颈。显然你从未测试过这个假设。我引用自己的话说:"另一方面,iostreams版本的速度为75.3 MB / s,无法快速缓冲数据以跟上硬盘的速度。这很糟糕,甚至还没有做任何真正的工作。我不知道。当我说我的I / O库应该能够使我的磁盘控制器饱和时,我认为我的期望值太高了。 @BenVoigt:我承认,我尽可能避免使用C ++。我尝试使用它很多,但它比我使用的其他编程语言更烦人,更难维护。这是我避免使用C ++的另一个原因 - 这甚至不是很快(甚至不是iostream) - 在大多数实现中,整个C ++库都很慢,可能除了std::sort之外,与qsort相比,它有点惊人的速度(2次),以可执行文件的大小为代价)。 在使用cout时,这里没有人提到并行环境中的问题。 @NicholasHamilton:只是不要在多个线程上打印。这适用于std::cout和printf。如果您使用多线程来处理非功能性代码(带有副作用),那么您将处理其他更严重的问题。锁没有帮助,它们只是使代码比单线程版本的代码慢。不测量螺纹,不要使用螺纹来提高性能。 你的表现论证没有任何意义。程序中的更多程序集并不意味着程序会变慢,因为你没有考虑所有产生printf函数的代码,这是很多代码。在我看来,可以使用 @ Ignas2526:虽然如此,但值得注意的是C和C ++标准库的实现可能缓存在内存中。但是,用C或C ++实现的庞大程序可能不是。二进制大小会影响性能,尤其是启动性能。此外,operator作为单独的函数调用实现,编译器必须知道operator对文件句柄对象的作用以优化任何内容,这很棘手。对于编译器来说就像write(write(write(write(out,"0x"), hex), number), endl)。 @xfix:printf()如何也是可扩展的? @PravasiMeet stackoverflow.com/questions/9260170/ 我喜欢这个答案的很多东西,但也许我最喜欢的部分是"每个人都认为他们关心性能,但没有人愿意测量它。" 值得一提的是,C ++编译器通常花费很长时间来解决iostream的重载插入和提取操作符,我读过大约33%的编译时间。 c_str是std::string获取C字符串的方法,而不是to_c。除此之外,这是一个踢屁股的答案。 @ user694733谢谢,修复:)。 如果I / O是瓶颈,那么它就是你应该花时间加速的程序的一部分! 关于时间这被标记为已接受的答案,不是吗? ;-) cout的东西几乎总是更烦人的打字,这很可能是我唯一关心的事情。如果我想混合很多变量,我需要大量的;lt;;lt;和大量的换行符。流语法也很奇怪;除了cout,我看到它几乎无处可用。不用了,谢谢。 我有很多情况,其中I / O是可测量的,而不是我的瓶颈。一个数据库。即使复制文件,pv也可以;甚至接近跟上我的磁盘,所以我使用dd代替并发送中断来获取状态。但是,如果你;#39;你的打印语句遇到了这个问题,而你;#39;不写一个;"shell程序;";像pv,其他可能是错误的。

从C ++ FAQ:

[15.1] Why should I use instead of the traditional ?

Increase type safety, reduce errors, allow extensibility, and provide inheritability.

printf() is arguably not broken, and scanf() is perhaps livable despite being error prone, however both are limited with respect to what C++ I/O can do. C++ I/O (using and >>) is, relative to C (using printf() and scanf()):

More type-safe: With , the type of object being I/O'd is known statically by the compiler. In contrast, uses"%" fields to figure out the types dynamically. Less error prone: With , there are no redundant "%" tokens that have to be consistent with the actual objects being I/O'd. Removing redundancy removes a class of errors. Extensible: The C++ mechanism allows new user-defined types to be I/O'd without breaking existing code. Imagine the chaos if everyone was simultaneously adding new incompatible"%" fields to printf() and scanf()?! Inheritable: The C++ mechanism is built from real classes such as std::ostream and std::istream. Unlike 's FILE*, these are real classes and hence inheritable. This means you can have other user-defined things that look and act like streams, yet that do whatever strange and wonderful things you want. You automatically get to use the zillions of lines of I/O code written by users you don't even know, and they don't need to know about your"extended stream" class.

另一方面,printf明显更快,这可能在非常具体和有限的情况下优先于cout使用它。始终首先介绍。 (例如,参见http://programming-designs.com/2009/02/c-speed-test-part-2-printf-vs-cout/)

相关讨论 另一方面,有FastFormat库(fastformat.org),可立即提供类型安全性,表现力和性能。 (不是我试过了......) 如此糟糕的复制粘贴片段,如果不考虑逃避(因此从文本中出现一些莫名其妙的遗漏),会得到九个赞成票吗? 还有Boost.Format,想知道两者如何比较。 @Marcelo Cantos:我怀疑对接受的答案有强烈抵制。 @xtofl是的,我刚才看了一眼。当我意识到这取决于环境变量时,看起来很有希望,但在我的烦恼表上被评为危险的高,这是我真正无需为我的代码编译而设置的一件事。 @jalf:我知道应该有一些东西......你介意加入这个旧的问题吗? stackoverflow.com/questions/446276/ @Marcelo可能因为这是一个很好的总结,引用了所有内容。格式化...是的,那很糟糕。我应该自己解决这个问题,但似乎其他人(包括你自己)会照顾它,当然,这比抱怨更具建设性。 到目前为止,printf()也应该是可扩展的。请参阅udrepper.livejournal.com/20948.html上的"printf hooks" @MaximYegorushkin:标准printf没有这样的能力。非可移植库机制几乎与iostream的完全标准化可扩展性处于同一水平。 "另一方面,printf明显更快"printf也更清洁,更容易使用,这就是为什么我尽可能避免cout。 "另一方面,printf明显更快,这可能证明使用它优先于cout"。这是真的?使用cin / scanf的等效声明是不正确的。请参阅stackoverflow.com/a/12762166/462335

人们经常声称printf要快得多。这在很大程度上是一个神话。我刚测试了它,结果如下:

123456789101112131415cout with only endl                     1461.310252 ms cout with only ' '                      343.080217 ms printf with only ' '                     90.295948 ms cout with string constant and endl      1892.975381 ms cout with string constant and ' '       416.123446 ms printf with string constant and ' '     472.073070 ms cout with some stuff and endl           3496.489748 ms cout with some stuff and ' '           2638.272046 ms printf with some stuff and ' '         2520.318314 ms

结论:如果只需要换行符,请使用printf;否则,cout几乎一样快,甚至更快。更多细节可以在我的博客上找到。

要清楚,我并不是说iostream总是优于printf;我只是想说你应该根据真实数据作出明智的决定,而不是基于一些常见的误导性假设的疯狂猜测。

更新:这是我用于测试的完整代码。用g++编译,没有任何附加选项(除了-lrt的时间)。

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384#include #include #include 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


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3