C ++中的’printf’与’cout’

'printf' vs. 'cout' in C++

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


我很惊讶这个问题中的每个人都声称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 ;lt;;lt; API。

通常,这意味着C版本会更短,但在大多数情况下它并不重要。打印多个参数时,差异很明显。如果您必须编写类似Error 2: File not found.的内容,假设错误编号,并且其描述是占位符,则代码将如下所示。两个示例的工作方式相同(好吧,实际上,std::endl刷新缓冲区)。

1
2
3
printf("Error %d: %s.
"
, id, errors[id]);
std::cout ;lt;;lt;"Error" ;lt;;lt; id ;lt;;lt;":" ;lt;;lt; errors[id] ;lt;;lt;"." ;lt;;lt; std::endl;

虽然这看起来并不太疯狂(只是它的两倍),但实际设置参数格式时,事情变得更加疯狂,而不仅仅是打印它们。例如,打印像0x0424这样的东西真是太疯狂了。这是由std::cout混合状态和实际值引起的。我从未见过类似std::setfill之类的语言(当然不是C ++)。 printf清楚地分隔了参数和实际类型。我真的更愿意维护它的printf版本(即使它看起来有点神秘)与iostream版本相比(因为它包含太多噪音)。

1
2
3
printf("0x%04x
"
, 0x424);
std::cout ;lt;;lt;"0x" ;lt;;lt; std::hex ;lt;;lt; std::setfill('0') ;lt;;lt; std::setw(4) ;lt;;lt; 0x424 ;lt;;lt; std::endl;

翻译

这就是printf的真正优势所在。 printf格式字符串很好......一个字符串。与operator ;lt;;lt;滥用iostream相比,这使得翻译变得非常容易。假设gettext()函数转换,并且您想要显示Error 2: File not found.,那么用于转换先前显示的格式字符串的代码将如下所示:

1
2
printf(gettext("Error %d: %s.
"
), id, errors[id]);

现在,让我们假设我们翻译为Fictionish,其中错误编号在描述之后。翻译后的字符串看起来像%2$s oru %1$d.
。现在,如何在C ++中完成它?好吧,我不知道。我想你可以伪造iostream构造printf,你可以传递给gettext或其他东西,以便进行翻译。当然,$不是C标准,但它很常见,在我看来它是安全的。

不必记住/查找特定的整数类型语法

C有很多整数类型,C ++也是如此。 std::cout为您处理所有类型,而printf需要特定语法,具体取决于整数类型(有非整数类型,但实际使用printf时唯一的非整数类型是const char *( C字符串,可以使用std::stringto_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编译)可能会更快。假设我的错误示??例,printf示例执行的调用少于cout示例。这是int mainprintf

1
2
3
4
5
6
7
8
9
10
11
12
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 ;lt;;lt;调用意味着使用另一组参数进行另一次调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
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 in 某些情况 - 以
结尾的常量字符串通常优化为puts

遗产

我不知道你为什么要继承ostream,但我不在乎。 它也可以用FILE

1
class MyFile : public FILE {}

类型安全

确实,可变长度参数列表没有安全性,但这并不重要,因为如果启用警告,流行的C编译器可以检测printf格式字符串的问题。 事实上,Clang可以在不启用警告的情况下做到这一点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ cat safety.c

#include ;lt;stdio.h;gt;

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 ++ FAQ:

[15.1] Why should I use instead of the traditional ?

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 , the type of object being I/O'd is
    known statically by the compiler. In
    contrast, uses"%" fields to
    figure out the types dynamically.
  • Less error prone: With , 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++ 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++ mechanism is built from real classes
    such as std::ostream and
    std::istream. Unlike '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要快得多。这在很大程度上是一个神话。我刚测试了它,结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
cout with only endl                     1461.310252 ms
cout with only '
'
                     343.080217 ms
printf with only '
'
                    90.295948 ms
cout with string constant and endl      1892.975381 ms
cout with string constant and '
'
      416.123446 ms
printf with string constant and '
'
    472.073070 ms
cout with some stuff and endl           3496.489748 ms
cout with some stuff and '
'
          2638.272046 ms
printf with some stuff and '
'
        2520.318314 ms

结论:如果只需要换行符,请使用printf;否则,cout几乎一样快,甚至更快。更多细节可以在我的博客上找到。

要清楚,我并不是说iostream总是优于printf;我只是想说你应该根据真实数据作出明智的决定,而不是基于一些常见的误导性假设的疯狂猜测。

更新:这是我用于测试的完整代码。用g++编译,没有任何附加选项(除了-lrt的时间)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#include <stdio.h>
#include <iostream>
#include <ctime>

class TimedSection {
    char const *d_name;
    timespec d_start;
    public:
        TimedSection(char const *name) :
            d_name(name)
        {
            clock_gettime(CLOCK_REALTIME, &d_start);
        }
        ~TimedSection() {
            timespec end;
            clock_gettime(CLOCK_REALTIME, &end);
            double duration = 1e3 * (end.tv_sec - d_start.tv_sec) +
                              1e-6 * (end.tv_nsec - d_start.tv_nsec);
            std::cerr << d_name << '\t' << std::fixed << duration <<" ms
"
;
        }
};

int main() {
    const int iters = 10000000;
    char const *text ="01234567890123456789";
    {
        TimedSection s("cout with only endl");
        for (int i = 0; i < iters; ++i)
            std::cout << std::endl;
    }
    {
        TimedSection s("cout with only '\
'"
);
        for (int i = 0; i < iters; ++i)
            std::cout << '
'
;
    }
    {
        TimedSection s("printf with only '\
'"
);
        for (int i = 0; i < iters; ++i)
            printf("
"
);
    }
    {
        TimedSection s("cout with string constant and endl");
        for (int i = 0; i < iters; ++i)
            std::cout <<"01234567890123456789" << std::endl;
    }
    {
        TimedSection s("cout with string constant and '\
'"
);
        for (int i = 0; i < iters; ++i)
            std::cout <<"01234567890123456789
"
;
    }
    {
        TimedSection s("printf with string constant and '\
'"
);
        for (int i = 0; i < iters; ++i)
            printf("01234567890123456789
"
);
    }
    {
        TimedSection s("cout with some stuff and endl");
        for (int i = 0; i < iters; ++i)
            std::cout << text <<"01234567890123456789" << i << std::endl;
    }
    {
        TimedSection s("cout with some stuff and '\
'"
);
        for (int i = 0; i < iters; ++i)
            std::cout << text <<"01234567890123456789" << i << '
'
;
    }
    {
        TimedSection s("printf with some stuff and '\
'"
);
        for (int i = 0; i < iters; ++i)
            printf("%s01234567890123456789%i
"
, text, i);
    }
}


我引述:

In high level terms, the main differences are type safety (cstdio
doesn't have it), performance (most iostreams implementations are
slower than the cstdio ones) and extensibility (iostreams allows
custom output targets and seamless output of user defined types).


一个是打印到stdout的函数。另一个是提供打印到stdout的operator<<的几个成员函数和重载的对象。我可以列举更多的差异,但我不确定你追求的是什么。


对我来说,真正的差异让我选择'cout'而不是'printf'是:

1)<<运算符可以为我的类重载。

2)cout的输出流可以很容易地更改为文件:
(: 复制粘贴 :)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include <fstream>
using namespace std;

int main ()
{
    cout <<"This is sent to prompt" << endl;
    ofstream file;
    file.open ("test.txt");
    streambuf* sbuf = cout.rdbuf();
    cout.rdbuf(file.rdbuf());
    cout <<"This is sent to file" << endl;
    cout.rdbuf(sbuf);
    cout <<"This is also sent to prompt" << endl;
    return 0;
}

3)我发现cout更具可读性,特别是当我们有很多参数时。

cout的一个问题是格式化选项。在printf中格式化数据(精度,对齐等)更容易。


这里没有另外提到的两点我觉得很重要:

1)如果你还没有使用STL,cout会携带很多行李。它为目标文件添加的代码数量是printf的两倍。对于string也是如此,这是我倾向于使用自己的字符串库的主要原因。

2)cout使用重载的;lt;;lt;运算符,我觉得这很不幸。如果您还将;lt;;lt;运算符用于其预期目的(左移),则会增加混淆。我个人不喜欢将操作员重载到与其预期用途相关的目的。

底线:如果我已经在使用STL,我将使用cout(和string)。否则,我倾向于避免它。


对于原语,使用哪一个并不重要。我说当你想要输出复杂的对象时它的用处。

例如,如果你有一个班级,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream>
#include <cstdlib>

using namespace std;

class Something
{
public:
        Something(int x, int y, int z) : a(x), b(y), c(z) { }
        int a;
        int b;
        int c;

        friend ostream& operator<<(ostream&, const Something&);
};

ostream& operator<<(ostream& o, const Something& s)
{
        o << s.a <<"," << s.b <<"," << s.c;
        return o;
}

int main(void)
{
        Something s(3, 2, 1);

        // output with printf
        printf("%i, %i, %i
"
, s.a, s.b, s.c);

        // output with cout
        cout << s << endl;

        return 0;
}

现在上面看起来似乎并不那么好,但我们假设您必须在代码中的多个位置输出它。不仅如此,我们假设您添加了一个字段"int d"。有了cout,你只需要在一个地方改变它。但是,使用printf,你必须在很多地方改变它,不仅如此,你必须提醒自己输出哪些。

有了这个说法,使用cout,您可以减少维护代码所花费的大量时间,而不仅仅是如果您在新应用程序中重新使用"Something"对象,您实际上不必担心输出。


当然,你可以写一些更好的东西来保持维护:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>
#include <cstdlib>

using namespace std;

class Something
{
    public:
        Something(int x, int y, int z) : a(x), b(y), c(z) { }
        int a;
        int b;
        int c;

        friend ostream& operator<<(ostream&, const Something&);

        void print() const { printf("%i, %i, %i
"
, a, b, c); }
};

ostream& operator<<(ostream& o, const Something& s)
{
    o << s.a <<"," << s.b <<"," << s.c;
    return o;
}

int main(void)
{
    Something s(3, 2, 1);

    // Output with printf
    s.print(); // Simple as well, isn't it?

    // Output with cout
    cout << s << endl;

    return 0;
}

并且对cout与printf进行了一些扩展测试,添加了一个'double'测试,如果有人想做更多的测试(VisualStudio2008,可执行文件的发布版本):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#include <stdio.h>
#include <iostream>
#include <ctime>

class TimedSection {
    char const *d_name;
    //timespec d_start;
    clock_t d_start;

    public:
        TimedSection(char const *name) :
            d_name(name)
        {
            //clock_gettime(CLOCK_REALTIME, &d_start);
            d_start = clock();
        }
        ~TimedSection() {
            clock_t end;
            //clock_gettime(CLOCK_REALTIME, &end);
            end = clock();
            double duration = /*1e3 * (end.tv_sec - d_start.tv_sec) +
                              1e-6 * (end.tv_nsec - d_start.tv_nsec);
                              */

                              (double) (end - d_start) / CLOCKS_PER_SEC;

            std::cerr << d_name << '\t' << std::fixed << duration * 1000.0 <<" ms
"
;
        }
};


int main() {
    const int iters = 1000000;
    char const *text ="01234567890123456789";
    {
        TimedSection s("cout with only endl");
        for (int i = 0; i < iters; ++i)
            std::cout << std::endl;
    }
    {
        TimedSection s("cout with only '\
'"
);
        for (int i = 0; i < iters; ++i)
            std::cout << '
'
;
    }
    {
        TimedSection s("printf with only '\
'"
);
        for (int i = 0; i < iters; ++i)
            printf("
"
);
    }
    {
        TimedSection s("cout with string constant and endl");
        for (int i = 0; i < iters; ++i)
            std::cout <<"01234567890123456789" << std::endl;
    }
    {
        TimedSection s("cout with string constant and '\
'"
);
        for (int i = 0; i < iters; ++i)
            std::cout <<"01234567890123456789
"
;
    }
    {
        TimedSection s("printf with string constant and '\
'"
);
        for (int i = 0; i < iters; ++i)
            printf("01234567890123456789
"
);
    }
    {
        TimedSection s("cout with some stuff and endl");
        for (int i = 0; i < iters; ++i)
            std::cout << text <<"01234567890123456789" << i << std::endl;
    }
    {
        TimedSection s("cout with some stuff and '\
'"
);
        for (int i = 0; i < iters; ++i)
            std::cout << text <<"01234567890123456789" << i << '
'
;
    }
    {
        TimedSection s("printf with some stuff and '\
'"
);
        for (int i = 0; i < iters; ++i)
            printf("%s01234567890123456789%i
"
, text, i);
    }
    {
        TimedSection s("cout with formatted double (width & precision once)");
        std::cout << std::fixed << std::scientific << std::right << std::showpoint;
        std::cout.width(8);
        for (int i = 0; i < iters; ++i)
            std::cout << text << 8.315 << i << '
'
;
    }
    {
        TimedSection s("cout with formatted double (width & precision on each call)");
        std::cout << std::fixed << std::scientific << std::right << std::showpoint;

        for (int i = 0; i < iters; ++i)
            { std::cout.width(8);
              std::cout.precision(3);
              std::cout << text << 8.315 << i << '
'
;
            }
    }
    {
        TimedSection s("printf with formatted double");
        for (int i = 0; i < iters; ++i)
            printf("%8.3f%i
"
, 8.315, i);
    }
}

结果是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
cout with only endl    6453.000000 ms
cout with only '
'
   125.000000 ms
printf with only '
'
   156.000000 ms
cout with string constant and endl    6937.000000 ms
cout with string constant and '
'
   1391.000000 ms
printf with string constant and '
'
   3391.000000 ms
cout with some stuff and endl    9672.000000 ms
cout with some stuff and '
'
   7296.000000 ms
printf with some stuff and '
'
   12235.000000 ms
cout with formatted double (width & precision once)    7906.000000 ms
cout with formatted double (width & precision on each call)    9141.000000 ms
printf with formatted double    3312.000000 ms


我想指出,如果你想在C ++中使用线程,如果使用cout,你可以获得一些有趣的结果。

考虑以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include ;lt;string;gt;
#include ;lt;iostream;gt;
#include ;lt;thread;gt;

using namespace std;

void task(int taskNum, string msg) {
    for (int i = 0; i ;lt; 5; ++i) {
        cout ;lt;;lt;"#" ;lt;;lt; taskNum ;lt;;lt;":" ;lt;;lt; msg ;lt;;lt; endl;
    }
}

int main() {
    thread t1(task, 1,"AAA");
    thread t2(task, 2,"BBB");
    t1.join();
    t2.join();
    return 0;
}

// g++ ./thread.cpp -o thread.out -ansi -pedantic -pthread -std=c++0x

现在,输出全部改组了。它也可以产生不同的结果,尝试多次执行:

1
2
3
4
5
6
7
8
9
##12::  ABABAB

##12::  ABABAB

##12::  ABABAB

##12::  ABABAB

##12::  ABABAB

您可以使用printf来正确使用它,也可以使用mutex

1
2
3
4
5
6
7
8
9
10
#1: AAA
#2: BBB
#1: AAA
#2: BBB
#1: AAA
#2: BBB
#1: AAA
#2: BBB
#1: AAA
#2: BBB

玩得开心!


TL; DR:在信任在线随机评论(包括此评论)之前,始终对所生成的机器代码大小,性能,可读性和编码时间进行自己的研究。

我不是专家。我碰巧听到两位同事在谈论我们应该如何避免因为性能问题而在嵌入式系统中使用C ++。好吧,有趣的是,我根据真正的项目任务做了一个基准测试。

在上述任务中,我们必须将一些配置写入RAM。就像是:

coffee=hot
sugar=none
milk=breast
mac=AA:BB:CC:DD:EE:FF

这是我的基准程序(是的,我知道OP询问了printf(),而不是fprintf()。尝试捕获本质,顺便说一句,OP的链接指向fprintf()。)

C程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
char coffee[10], sugar[10], milk[10];
unsigned char mac[6];

/* Initialize those things here. */

FILE * f = fopen("a.txt","wt");

fprintf(f,"coffee=%s
sugar=%s
milk=%s
mac=%02X:%02X:%02X:%02X:%02X:%02X
"
, coffee, sugar, milk, mac[0], mac[1],mac[2],mac[3],mac[4],mac[5]);

fclose(f);

C ++程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//Everything else is identical except:

std::ofstream f("a.txt", std::ios::out);

f ;lt;;lt;"coffee=" ;lt;;lt; coffee ;lt;;lt;"
"
;
f ;lt;;lt;"sugar=" ;lt;;lt; sugar ;lt;;lt;"
"
;
f ;lt;;lt;"milk=" ;lt;;lt; milk ;lt;;lt;"
"
;
f ;lt;;lt;"mac=" ;lt;;lt; (int)mac[0] ;lt;;lt;":"
    ;lt;;lt; (int)mac[1] ;lt;;lt;":"
    ;lt;;lt; (int)mac[2] ;lt;;lt;":"
    ;lt;;lt; (int)mac[3] ;lt;;lt;":"
    ;lt;;lt; (int)mac[4] ;lt;;lt;":"
    ;lt;;lt; (int)mac[5] ;lt;;lt; endl;
f.close();

在我将它们环绕100,000次之前,我尽力擦亮它们。结果如下:

C程序:

1
2
3
real    0m 8.01s
user    0m 2.37s
sys     0m 5.58s

C ++程序:

1
2
3
real    0m 6.07s
user    0m 3.18s
sys     0m 2.84s

对象文件大小:

1
2
C   - 2,092 bytes
C++ - 3,272 bytes

结论:在我非常具体的平台上,使用一个非常特定的处理器,运行一个非常特定的Linux内核版本,运行一个用特定版本的GCC编译的程序,为了完成一个非常具体的任务,我会说C ++方法更合适,因为它运行速度更快,并提供更好的可读性。另一方面,在我看来,C占用的空间很小,几乎没有任何意义,因为程序大小不是我们关心的问题。

记住,YMMV。


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

和。

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

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

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

没有相当于"% d"的iostream


我不是程序员,但我是一名人工因素工程师。我觉得编程语言应该易于学习,理解和使用,这要求它具有简单一致的语言结构。虽然所有的语言都是象征性的,因此,在其核心,任意的,有一些惯例,并遵循它们使语言更容易学习和使用。

C ++和其他语言中有大量函数作为函数(参数)编写,这种语法最初用于计算机时代的数学函数关系。 printf()遵循这种语法,如果C ++的编写者想要创建任何逻辑上不同的读写文件的方法,他们可以使用类似的语法简单地创建一个不同的函数。

在Python中,我们当然可以使用相当标准的object.method语法进行打印,即variablename.print,因为变量是对象,但在C ++中它们不是。

我不喜欢cout语法,因为; lt ;; lt;运营商不遵守任何规则。它是一种方法或功能,即它需要一个参数并对其做一些事情。然而,它被写成好像是一个数学比较运算符。从人为因素的角度来看,这是一种糟糕的方法。


我想说printf的可扩展性缺乏并不完全正确:
在C中,这是事实。但在C语言中,没有真正的课程。
在C ++中,可以重载强制转换运算符,因此,重载char*运算符并使用printf这样:

1
2
3
Foo bar;
...;
printf("%s",bar);

如果Foo超载好运算符,则是可能的。或者如果你做了一个好方法。简而言之,对于我来说,printf对于cout是可扩展的。

我可以看到C ++流的技术论点(通常......不仅仅是cout。)是:

  • 类型安全。 (顺便说一下,如果我要打印一个'
    '
    我使用putchar('
    ')
    ......我不会用核弹来杀死昆虫。)

  • 更容易学习。 (没有"复杂"的参数可供学习,只需使用<<>>运算符)

  • 使用std::string本地工作(printfstd::string::c_str(),但scanf?)

对于printf,我看到:

  • 更容易,或至少更短(在写字符方面)复杂的格式。对我来说更具可读性(我猜的是味道问题)。

  • 更好地控制函数的作用(返回写入的字符数和%n格式化程序:"没有打印。参数必须是指向signed int的指针,其中存储了到目前为止写入的字符数。" (来自printf - C ++参考)

  • 更好的调试可能性出于与上一个论点相同的原因。

我的个人喜好转到printf(和scanf)函数,主要是因为我喜欢短行,因为我不认为打印文本的类型问题真的很难避免。
我唯一对C风格函数感到遗憾的是不支持std::string。在将它提供给printf之前我们必须经过char*(如果我们想要读取,那么std::string::c_str(),但是如何写?)


1
2
cout<<"Hello";
printf("%s","Hello");

两者都用于打印值。它们的语法完全不同。 C ++有两个,C
只有printf。


printf是函数,而cout是变量。