#1楼
更多差异:“printf”返回一个整数值(等于打印的字符数),“cout”不返回任何内容
和。
cout << "y = " << 7;
不是原子的。
printf("%s = %d", "y", 7);
是原子的。
cout执行类型检查,printf没有。
没有相当于"% d"
iostream
#2楼
令我感到惊讶的是,这个问题中的每个人都声称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 <<
API返回自身。
通常,这意味着C版本会更短,但在大多数情况下它并不重要。 打印多个参数时,差异很明显。 如果你必须写Error 2: File not found.
,假设错误号,并且其描述是占位符,代码将如下所示。 两个示例的工作方式相同 (好吧,有点, std::endl
实际上刷新了缓冲区)。
printf("Error %d: %s.\n", id, errors[id]);
std::cout << "Error " << id << ": " << errors[id] << "." << std::endl;
虽然这看起来并不太疯狂(只是它的两倍),但实际设置参数格式时,事情变得更加疯狂,而不仅仅是打印它们。 例如,打印像0x0424
这样的东西0x0424
太疯狂了。 这是由std::cout
混合状态和实际值引起的。 我从未见过像std::setfill
这样的语言(当然不是C ++)。 printf
清楚地分隔了参数和实际类型。 我真的更喜欢维护它的printf
版本(即使它看起来有点神秘)与iostream
版本相比(因为它包含太多的噪音)。
printf("0x%04x\n", 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.
,获取前面显示的格式字符串的翻译代码如下所示:
printf(gettext("Error %d: %s.\n"), id, errors[id]);
现在,让我们假设我们翻译为Fictionish,其中错误编号在描述之后。 翻译的字符串看起来像%2$s oru %1$d.\\n
。 现在,如何在C ++中完成它? 好吧,我不知道。 我想你可以制作假的iostream
,它可以构造printf
,你可以传递给gettext
或者其他东西,用于翻译。 当然, $
不是C标准,但它很常见,在我看来它是安全的。
不必记住/查找特定的整数类型语法
C有很多整数类型,C ++也是如此。 std::cout
为你处理所有类型,而printf
需要特定的语法,具体取决于整数类型(有非整数类型,但实际上你将在printf
使用的唯一非整数类型是const char *
(C string,可以使用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
编译器选项)。 假设我的错误示例, printf
示例的执行方式比cout
示例少。 这是printf
int main
:
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 <<
call表示另一个带有另一组参数的调用。
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
- 常量字符串以\\n
结尾) \\n
通常被优化为puts
)。
遗产
我不知道你为什么要继承ostream
,但我不在乎。 它也可以用FILE
。
class MyFile : public FILE {}
类型安全
确实,可变长度参数列表没有安全性,但这并不重要,因为如果启用警告,流行的C编译器可以检测printf
格式字符串的问题。 事实上,Clang可以在不启用警告的情况下做到这一点。
$ 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);
^
#3楼
printf
是一个函数,而cout
是一个变量。
#4楼
这里没有另外提到的两点我觉得很重要:
1)如果你还没有使用STL, cout
携带很多行李。 它为您的目标文件添加了两倍于printf
代码。 对于string
也是如此,这是我倾向于使用自己的字符串库的主要原因。
2) cout
使用重载的<<
运算符,我觉得不幸。 如果您还将<<
运算符用于其预期目的(左移),则会增加混淆。 我个人不喜欢将操作员重载到与其预期用途相关的目的。
底线:如果我已经在使用STL,我将使用cout
(和string
)。 否则,我倾向于避免它。
#5楼
我引述 :
在高级方面,主要区别在于类型安全(cstdio没有它),性能(大多数iostream实现比cstdio慢)和可扩展性(iostream允许自定义输出目标和用户定义类型的无缝输出)。
来源:oschina
链接:https://my.oschina.net/u/3797416/blog/3195221