printf()和cout在c++中的区别是什么?
当前回答
来自c++常见问题解答:
[15.1] Why should I use <iostream> instead of the traditional <cstdio>? 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 <iostream>, the type of object being I/O'd is known statically by the compiler. In contrast, <cstdio> uses "%" fields to figure out the types dynamically. Less error prone: With <iostream>, 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++ <iostream> 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++ <iostream> mechanism is built from real classes such as std::ostream and std::istream. Unlike <cstdio>'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/)
其他回答
更多的差异: "printf"返回一个整数值(等于打印的字符数),"cout"不返回任何东西
And.
Cout << "y = " << 7;不是原子的。
Printf ("%s = %d", "y", 7);是原子的。
Cout执行类型检查,printf不执行。
iostream中没有% d的等价物
Printf是一个函数而cout是一个变量。
我不是程序员,但我曾是人为因素工程师。我觉得编程语言应该是容易学习、理解和使用的,这就要求它有一个简单而一致的语言结构。虽然所有的语言都是象征性的,因此,在其核心是任意的,但有惯例,遵循这些惯例使语言更容易学习和使用。
在c++和其他语言中,有大量的函数以函数(参数)的形式编写,这种语法最初是在前计算机时代用于数学中的函数关系。printf()遵循这种语法,如果c++的作者想要创建任何逻辑上不同的方法来读写文件,他们可以使用类似的语法简单地创建一个不同的函数。
在Python中,我们当然可以使用同样标准的对象进行打印。方法语法,即variablename。因为变量是对象,但在c++中它们不是。
我不喜欢cout语法,因为<<操作符不遵循任何规则。它是一个方法或函数,也就是说,它接受一个参数并对它做一些事情。然而,它被写成了一个数学比较运算符。从人为因素的角度来看,这是一个糟糕的方法。
这里没有提到的两点我认为很重要:
1)如果你还没有使用STL, cout会携带很多包袱。它向目标文件中添加的代码是printf的两倍多。对于字符串也是如此,这也是我倾向于使用自己的字符串库的主要原因。
2) cout使用重载的<<操作符,我觉得这很不幸。如果还将<<运算符用于其预期目的(左移),则会增加混淆。我个人不喜欢为了与其预期用途无关的目的而重载操作符。
底线:如果我已经在使用STL,我将使用cout(和字符串)。否则,我倾向于避免它。
我很惊讶这个问题中的每个人都声称std::cout比printf好得多,即使这个问题只是询问了差异。现在,有一个区别- std::cout是c++,而printf是C(然而,你可以在c++中使用它,就像C中的几乎任何其他东西一样)。printf和std::cout都有各自的优点。
真正的差异
可扩展性
cout是可扩展的。我知道人们会说printf也是可扩展的,但是这样的扩展在C标准中没有提到(所以你必须使用非标准的特性——但甚至不存在常见的非标准特性),而且这样的扩展是一个字母(所以很容易与已有的格式发生冲突)。
与printf不同,std::cout完全依赖于运算符重载,因此自定义格式没有问题——你所要做的就是定义一个子例程,将std::ostream作为第一个参数,将你的类型作为第二个参数。因此,不存在命名空间问题——只要你有一个类(它不局限于一个字符),你就可以对它进行std::ostream重载。
然而,我怀疑很多人会想要扩展ostream(老实说,我很少看到这样的扩展,即使它们很容易创建)。不过,如果你需要的话,它就在这里。
语法
很容易注意到,printf和std::cout使用不同的语法。Printf使用使用模式字符串和变长参数列表的标准函数语法。实际上,printf是C语言有它们的原因之一——printf格式太复杂了,没有它们就无法使用。然而,std::cout使用了不同的API——操作符<< API返回自身。
一般来说,这意味着C版本会更短,但在大多数情况下,这并不重要。当打印许多参数时,这种差异很明显。如果你必须写类似错误2:文件未找到。,假设错误编号,并且它的描述是占位符,代码看起来像这样。这两个例子的工作原理是一样的(嗯,有点像std::endl实际上会刷新缓冲区)。
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;
翻译
这就是printf的真正优势所在。printf格式字符串很好…一个字符串。与操作符<<滥用iostream相比,这使得它非常容易翻译。假设gettext()函数进行了转换,并且希望显示Error 2: File not found。,获得前面显示的格式字符串的翻译的代码看起来像这样:
printf(gettext("Error %d: %s.\n"), id, errors[id]);
现在,让我们假设我们转换为Fictionish,其中错误编号在描述之后。翻译后的字符串看起来像%2$s或u %1$d.\n。现在,如何在c++中做到这一点?嗯,我不知道。我猜你可以伪造iostream,它构造printf,你可以传递给gettext,或者其他东西,用于翻译。当然,$不是C语言的标准,但在我看来,它很常见,使用起来是安全的。
不需要记住/查找特定的整数类型语法
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.
你不能打印NUL字节\0
因为printf使用C字符串而不是c++字符串,所以如果没有特定的技巧,它无法打印NUL字节。在某些情况下,可以使用%c和'\0'作为参数,尽管这显然是一种hack。
没人关心的差异
性能
更新:原来iostream是如此之慢,它通常比你的硬盘驱动器(如果你重定向你的程序到文件)。如果需要输出大量数据,禁用stdio同步可能会有所帮助。如果性能是一个真正的问题(而不是写几行到STDOUT),只需使用printf。
每个人都认为他们关心绩效,但没有人费心去衡量它。我的答案是无论你使用printf还是iostream, I/O都是瓶颈。我认为printf可以更快从快速查看汇编(与clang编译使用-O3编译器选项)。假设我的错误示例,printf示例比cout示例调用更少。这是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 (number)作为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
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结尾的常量字符串通常被优化为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);
^
推荐文章
- 为什么我的程序不能在Windows 7下用法语编译?
- 如何获取变量的类型?
- 什么是奇怪的重复模板模式(CRTP)?
- 连接两个向量的最佳方法是什么?
- 在c++中,是通过值传递更好,还是通过引用到const传递更好?
- 在STL中deque到底是什么?
- Windows上最好的免费c++分析器是什么?
- 如何自动转换强类型枚举为int?
- 在一个类中使用具有成员函数的泛型std::function对象
- 'for'循环中的后增量和前增量产生相同的输出
- 虚函数和纯虚函数的区别
- c++中的_tmain()和main()有什么区别?
- 内存泄漏是否正常?
- 当启用c++ 11时,std::vector性能回归
- 什么时候使用哪种指针?