C ++中的'printf'与'cout'

时间秒杀一切 提交于 2020-03-15 10:38:20

在C ++中printf()cout什么区别?


#1楼

更多差异:“printf”返回一个整数值(等于打印的字符数),“cout”不返回任何内容

和。

cout << "y = " << 7; 不是原子的。

printf("%s = %d", "y", 7); 是原子的。

cout执行类型检查,printf没有。

没有相当于"% d" iostream


#2楼

令我感到惊讶的是,这个问题中的每个人都声称std::coutprintf更好,即使这个问题只是要求差异。 现在,有一个区别 - std::cout是C ++,而printf是C(但是,你可以在C ++中使用它,就像C中几乎所有其他东西一样)。 现在,我会在这里说实话; printfstd::cout都有它们的优点。

真正的差异

可扩展性

std::cout是可扩展的。 我知道人们会说printf也是可扩展的,但是C标准中没有提到这样的扩展(所以你必须使用非标准功能 - 但是甚至不存在常见的非标准功能),并且这样的扩展是一个字母(因此很容易与已存在的格式冲突)。

printf不同, std::cout完全依赖于运算符重载,因此自定义格式没有问题 - 您所做的就是定义一个子程序,将std::ostream作为第一个参数,将您的类型作为第二个参数。 因此,没有命名空间问题 - 只要你有一个类(不限于一个字符),你可以为它工作std::ostream重载。

但是,我怀疑很多人会想要扩展ostream (说实话,我很少看到这样的扩展,即使它们很容易制作)。 但是,如果你需要它就在这里。

句法

因为它很容易被注意到, printfstd::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/fprintfhttp://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允许自定义输出目标和用户定义类型的无缝输出)。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!