问题描述
printf()
<有什么区别/a> 和 cout
在 C++ 中?>
推荐答案
我很惊讶这个问题中的每个人都声称 std::cout
比 printf
好得多>,即使问题只是要求差异.现在,有一个区别 - std::cout
是 C++,而 printf
是 C(但是,您可以在 C++ 中使用它,就像 几乎 来自 C) 的任何其他内容.现在,我会在这里诚实;printf
和 std::cout
各有优势.
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.
std::cout
是可扩展的.我知道人们会说 printf
也是可扩展的,但是 C 标准中没有提到这种扩展(所以你必须使用非标准特性 - 但甚至不存在常见的非标准特性),并且这样的扩展名是一个字母(所以很容易与已经存在的格式冲突).
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).
与 printf
不同,std::cout
完全依赖于运算符重载,因此自定义格式没有问题 - 您所做的只是定义一个采用 的子例程std::ostream
作为第一个参数,您的类型作为第二个参数.因此,不存在命名空间问题——只要您有一个类(不限于一个字符),您就可以为它重载 std::ostream
.
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.
然而,我怀疑很多人会想要扩展 ostream
(说实话,我很少看到这样的扩展,即使它们很容易制作).但是,如果您需要它,它就在这里.
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.
很容易注意到,printf
和 std::cout
使用不同的语法.printf
使用使用模式字符串和可变长度参数列表的标准函数语法.实际上,printf
是 C 拥有它们的一个原因——printf
格式太复杂了,没有它们就无法使用.但是,std::cout
使用不同的 API - operator <<
API 返回自身.
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.
通常,这意味着 C 版本会更短,但在大多数情况下,这无关紧要.当您打印许多参数时,差异很明显.如果您必须编写类似 Error 2: File not found.
的内容,假设错误编号,并且其描述是占位符,则代码将如下所示.两个示例 工作相同(嗯,有点,std::endl
实际上刷新了缓冲区).
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.
", id, errors[id]);
std::cout << "Error " << id << ": " << errors[id] << "." << std::endl;
虽然这看起来并不太疯狂(只是长了两倍),但当您实际格式化参数而不是仅仅打印它们时,事情会变得更加疯狂.例如,打印诸如 0x0424
之类的东西就太疯狂了.这是由 std::cout
混合状态和实际值引起的.我从未见过像 std::setfill
这样的语言是一种类型(当然,除了 C++).printf
清楚地将参数和实际类型分开.与 iostream
版本相比,我真的更愿意维护它的 printf
版本(即使它看起来有点神秘)(因为它包含太多噪音).
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
", 0x424);
std::cout << "0x" << std::hex << std::setfill('0') << std::setw(4) << 0x424 << std::endl;
翻译
这就是 printf
的真正优势所在.printf
格式字符串很好......一个字符串.与 operator <<
滥用 iostream
相比,这使得翻译变得非常容易.假设 gettext()
函数进行了翻译,并且您想显示 Error 2: File not found.
,则获取先前显示的格式字符串翻译的代码将如下所示这个:
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.
"), id, errors[id]);
现在,让我们假设我们翻译成 Fictionish,其中错误编号在描述之后.翻译后的字符串看起来像 %2$s oru %1$d.
.现在,如何在 C++ 中做到这一点?好吧,我不知道.我想你可以制作伪造的 iostream
来构造 printf
,你可以将它传递给 gettext
或其他东西,以用于翻译.当然,$
不是 C 标准,但它很常见,在我看来它可以安全使用.
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.
. 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.
C 有很多整数类型,C++ 也是如此.std::cout
为您处理所有类型,而 printf
需要取决于整数类型的特定语法(有非整数类型,但您唯一的非整数类型)将在实践中使用 printf
是 const char *
(C 字符串,可以使用 std::string<的
to_c
方法获得/代码>)).例如,要打印size_t
,您需要使用%zu
,而int64_t
将需要使用%"PRId64"代码>.这些表格可在 http://en.cppreference.com/w/cpp/io/c/fprintf 和 http://en.cppreference.com/w/cpp/types/integer.
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 %zu
, 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.
因为 printf
使用 C 字符串而不是 C++ 字符串,所以它不能在没有特定技巧的情况下打印 NUL 字节.在某些情况下,可以将 %c
与 ' '
一起用作参数,尽管这显然是一种黑客行为.
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 ' '
as an argument, although that's clearly a hack.
更新:事实证明 iostream
太慢了,它通常比您的硬盘驱动器慢(如果您将程序重定向到文件).如果您需要输出大量数据,禁用与 stdio
的同步可能会有所帮助.如果性能是一个真正的问题(而不是将几行写入 STDOUT),只需使用 printf
.
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
.
每个人都认为他们关心性能,但没有人费心去衡量它.我的回答是 I/O 无论如何都是瓶颈,无论你使用 printf
还是 iostream
.我认为 printf
可以更快地从快速查看汇编(使用 -O3
编译器选项使用 clang 编译).假设我的错误示例,printf
示例比 cout
示例执行更少的调用.这是 int main
和 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:
您可以很容易地注意到两个字符串和 2
(数字)作为 printf
参数被推送.就是这样;没有别的了.为了比较,这是编译成汇编的iostream
.不,没有内联;每一个 operator <<
调用都意味着另一个带有另一组参数的调用.
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:
但是,老实说,这没有任何意义,因为无论如何 I/O 都是瓶颈.我只是想表明 iostream
不是更快,因为它是类型安全的".大多数 C 实现使用计算的 goto 实现 printf
格式,因此 printf
尽可能快,即使编译器不知道 printf
(并不是说它们不是 - 有些编译器可以在某些情况下优化 printf
- 以
结尾的常量字符串通常被优化为 puts
).
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
is usually optimized to puts
).
我不知道你为什么要继承 ostream
,但我不在乎.FILE
也可以.
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 {}
类型安全
确实,可变长度参数列表没有安全性,但这并不重要,因为如果启用警告,流行的 C 编译器可以检测到 printf
格式字符串的问题.事实上,Clang 可以在不启用警告的情况下做到这一点.
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
", 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);
^
这篇关于C++ 中的“printf"与“cout"的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!