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

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

问题描述

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屋!

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