C ++中的'printf'与'cout' [英] 'printf' vs. 'cout' in C++
问题描述
printf()有什么区别
和 cout
在C ++中?
我很惊讶这个问题中的每个人都声称 std :: cout
比 printf
更好,即使这个问题只是要求差异。现在,有一个区别- std :: cout
是C ++, printf
是C(但是,您可以使用就像C中的几乎一样。现在,我将在这里诚实。 printf
和 std :: cout
都有其优势。
< h1>实际差异
可扩展性
std :: cout
是可扩展的。我知道人们会说 printf
也是可扩展的,但是C标准中没有提到这种扩展(因此您必须使用非标准功能-但甚至不能使用
与不同,这样的扩展名是一个字母(因此很容易与现有格式冲突)。
printf , std :: cout
完全取决于运算符的重载,因此自定义格式没有问题-您要做的就是定义一个使用 std :: ostream
作为第一个参数,您的类型作为第二个参数。这样,就没有名称空间问题-只要您有一个类(不限于一个字符),就可以对其进行 std :: ostream
重载。
但是,我怀疑很多人是否想扩展 ostream
(说实话,我很少看到这样的内容。扩展,即使它们很容易实现也是如此)。但是,如果需要的话,就在这里。
语法
很容易注意到,两个 printf
和 std :: cout
使用不同的语法。 printf
使用标准函数语法,该语法使用模式字符串和变长参数列表。实际上, printf
是C拥有它们的原因- printf
格式过于复杂,无法使用它们。但是, std :: cout
使用不同的API-操作符<<
返回自身的API。
通常,这意味着C版本会更短,但是在大多数情况下都没有关系。当您打印许多参数时,差异是显而易见的。如果您必须编写类似 Error 2:File not found。
的错误,假定错误编号,且其描述为占位符,则代码应如下所示。这两个示例均能正常工作(嗯,有点, std :: endl
实际上刷新缓冲区)。
printf(错误%d:%s.\n,id,错误[ID]);
std :: cout<< 错误<< id<< :<< errors [id]<< 。 << std :: endl;
虽然这看起来不太疯狂(只是更长的两倍),但当事情变得更疯狂时您实际上是在格式化参数,而不仅仅是打印它们。例如,打印诸如 0x0424
之类的东西真是疯狂。这是由 std :: cout
混合状态和实际值引起的。我从未见过像 std :: setfill
这样的语言是一种类型的语言(当然不是C ++)。 printf
清楚地将参数和实际类型分开。与 iostream
版本相比,我真的更喜欢维护它的 printf
版本(即使看起来有些神秘)。
printf( 0x%04x\n,0x424);
std :: cout<< 0x<< std :: hex<< std :: setfill('0')<< std :: setw(4)<< 0x424<< std :: endl;
翻译
这是真正的 printf
的优势在于。 printf
格式的字符串很好...一个字符串。与运算符<<
滥用 iostream
相比,这真的很容易翻译。假定 gettext()
函数已翻译,并且您想显示错误2:找不到文件。
,代码以获得先前显示的格式字符串的翻译如下:
printf(gettext( Error%d:%s。 \n),id,错误[id]);
现在,假设我们翻译成Fictionish,错误号在描述之后。转换后的字符串看起来像%2 $ s oru%1 $ d.\n
。现在,如何在C ++中做到这一点?好吧,我不知道。我想您可以制作伪造的 iostream
来构造 printf
并传递给 gettext
之类的东西,用于翻译。当然, $
不是C标准,但是它是如此普遍,以至于我认为可以安全使用。
不必记住/查找特定的整数类型语法
C有很多整数类型,C ++也是如此。 std :: cout
可以为您处理所有类型,而 printf
则需要根据整数类型的特定语法(非整数-integer类型,但实际上将与 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编译) c $ c>编译器选项)。假定我的错误示例,与
cout
示例相比, printf
示例的调用方式更少。这是 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
(数字)作为 printf
参数被压入。就是这样;没有别的了。为了进行比较,这是 iostream
编译为程序集。不,没有内联;每个单个操作符<<
的调用都意味着另一个调用带有另一组参数。
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
#2 b bl _ZNSolsEi
ldr r1,.LCPI0_2
mov r2,#2
mov r3,#0
mov r4,r0
bl _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_B $ 1T ,.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
,r0 b sub r0,r0,#24
ldr r0,[r0]
加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
b 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
-常量字符串结尾 \n
通常被优化为 puts
)。
< h2>继承
我不知道您为什么要继承 ostream
,但我不在乎。 FILE
也可能。
class MyFile:公共FILE {}
类型安全性
真正的可变长度参数列表没有安全性,但是没关系,因为如果启用警告,流行的C编译器可以检测到 printf
格式字符串的问题。实际上,Clang可以在不启用警告的情况下做到这一点。
$猫安全。c
#include < stdio.h>
int main(void){
printf( String:%s\n,42);
返回0;
}
$ clang safety.c
safety.c:4:28:警告:格式指定为'char *'类型,但参数的类型为'int '[-Wformat]
printf( String:%s\n,42);
~~ ^〜
%d
产生1条警告。
$ gcc -Wall safety.c
safety.c:在函数'main'中:
safety.c:4:5:警告:格式'%s'期望为'char类型的参数*,但参数2的类型为'int'[-Wformat =]
printf( String:%s\n,42);
^
What is the difference between printf()
and cout
in C++?
I'm surprised that everyone in this question claims that std::cout
is way better than printf
, even if the question just asked for differences. Now, there is a difference - std::cout
is C++, and printf
is C (however, you can use it in C++, just like almost anything else from C). Now, I'll be honest here; both printf
and std::cout
have their advantages.
Real differences
Extensibility
std::cout
is extensible. I know that people will say that printf
is extensible too, but such extension is not mentioned in the C standard (so you would have to use non-standard features - but not even common non-standard feature exists), and such extensions are one letter (so it's easy to conflict with an already-existing format).
Unlike printf
, std::cout
depends completely on operator overloading, so there is no issue with custom formats - all you do is define a subroutine taking std::ostream
as the first argument and your type as second. As such, there are no namespace problems - as long you have a class (which isn't limited to one character), you can have working std::ostream
overloading for it.
However, I doubt that many people would want to extend ostream
(to be honest, I rarely saw such extensions, even if they are easy to make). However, it's here if you need it.
Syntax
As it could be easily noticed, both printf
and std::cout
use different syntax. printf
uses standard function syntax using pattern string and variable-length argument lists. Actually, printf
is a reason why C has them - printf
formats are too complex to be usable without them. However, std::cout
uses a different API - the operator <<
API that returns itself.
Generally, that means the C version will be shorter, but in most cases it won't matter. The difference is noticeable when you print many arguments. If you have to write something like Error 2: File not found.
, assuming error number, and its description is placeholder, the code would look like this. Both examples work identically (well, sort of, std::endl
actually flushes the buffer).
printf("Error %d: %s.\n", id, errors[id]);
std::cout << "Error " << id << ": " << errors[id] << "." << std::endl;
While this doesn't appear too crazy (it's just two times longer), things get more crazy when you actually format arguments, instead of just printing them. For example, printing of something like 0x0424
is just crazy. This is caused by std::cout
mixing state and actual values. I never saw a language where something like std::setfill
would be a type (other than C++, of course). printf
clearly separates arguments and actual type. I really would prefer to maintain the printf
version of it (even if it looks kind of cryptic) compared to iostream
version of it (as it contains too much noise).
printf("0x%04x\n", 0x424);
std::cout << "0x" << std::hex << std::setfill('0') << std::setw(4) << 0x424 << std::endl;
Translation
This is where the real advantage of printf
lies. The printf
format string is well... a string. That makes it really easy to translate, compared to operator <<
abuse of iostream
. Assuming that the gettext()
function translates, and you want to show Error 2: File not found.
, the code to get translation of the previously shown format string would look like this:
printf(gettext("Error %d: %s.\n"), id, errors[id]);
Now, let's assume that we translate to Fictionish, where the error number is after the description. The translated string would look like %2$s oru %1$d.\n
. Now, how to do it in C++? Well, I have no idea. I guess you can make fake iostream
which constructs printf
that you can pass to gettext
, or something, for purposes of translation. Of course, $
is not C standard, but it's so common that it's safe to use in my opinion.
Not having to remember/look-up specific integer type syntax
C has lots of integer types, and so does C++. std::cout
handles all types for you, while printf
requires specific syntax depending on an integer type (there are non-integer types, but the only non-integer type you will use in practice with printf
is const char *
(C string, can be obtained using to_c
method of std::string
)). For instance, to print size_t
, you need to use %zd
, while int64_t
will require using %"PRId64"
. The tables are available at http://en.cppreference.com/w/cpp/io/c/fprintf and http://en.cppreference.com/w/cpp/types/integer.
You can't print the NUL byte, \0
Because printf
uses C strings as opposed to C++ strings, it cannot print NUL byte without specific tricks. In certain cases it's possible to use %c
with '\0'
as an argument, although that's clearly a hack.
Differences nobody cares about
Performance
Update: It turns out that iostream
is so slow that it's usually slower than your hard drive (if you redirect your program to file). Disabling synchronization with stdio
may help, if you need to output lots of data. If the performance is a real concern (as opposed to writing several lines to STDOUT), just use printf
.
Everyone thinks that they care about performance, but nobody bothers to measure it. My answer is that I/O is bottleneck anyway, no matter if you use printf
or iostream
. I think that printf
could be faster from a quick look into assembly (compiled with clang using the -O3
compiler option). Assuming my error example, printf
example does way fewer calls than the cout
example. This is int main
with 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:
You can easily notice that two strings, and 2
(number) are pushed as printf
arguments. That's about it; there is nothing else. For comparison, this is iostream
compiled to assembly. No, there is no inlining; every single operator <<
call means another call with another set of arguments.
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:
However, to be honest, this means nothing, as I/O is the bottleneck anyway. I just wanted to show that iostream
is not faster because it's "type safe". Most C implementations implement printf
formats using computed goto, so the printf
is as fast as it can be, even without compiler being aware of printf
(not that they aren't - some compilers can optimize printf
in certain cases - constant string ending with \n
is usually optimized to puts
).
Inheritance
I don't know why you would want to inherit ostream
, but I don't care. It's possible with FILE
too.
class MyFile : public FILE {}
Type safety
True, variable length argument lists have no safety, but that doesn't matter, as popular C compilers can detect problems with printf
format string if you enable warnings. In fact, Clang can do that without enabling warnings.
$ 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 ++中的'printf'与'cout'的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!