C++ 中的“printf"与“cout" [英] 'printf' vs. 'cout' in C++

查看:38
本文介绍了C++ 中的“printf"与“cout"的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

printf()<有什么区别/a> 和 cout 在 C++ 中?>

推荐答案

我很惊讶这个问题中的每个人都声称 std::coutprintf 好得多>,即使问题只是要求差异.现在,有一个区别 - std::cout 是 C++,而 printf 是 C(但是,您可以在 C++ 中使用它,就像 几乎 来自 C) 的任何其他内容.现在,我会在这里诚实;printfstd::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.

很容易注意到,printfstd::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 需要取决于整数类型的特定语法(有非整数类型,但您唯一的非整数类型)将在实践中使用 printfconst char * (C 字符串,可以使用 std::string<的 to_c 方法获得/代码>)).例如,要打印size_t,您需要使用%zu,而int64_t 将需要使用%"PRId64".这些表格可在 http://en.cppreference.com/w/cpp/io/c/fprintfhttp://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 mainprintf:

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"的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆