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

后端 未结 16 1568
梦如初夏
梦如初夏 2020-11-22 07:04

What is the difference between printf() and cout in C++?

相关标签:
16条回答
  • 2020-11-22 07:17

    Of course you can write "something" a bit better to keep maintenance:

    #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\n", 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;
    }
    

    And a bit extended test of cout vs. printf, added a test of 'double', if anyone wants to do more testing (Visual Studio 2008, release version of the executable):

    #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\n";
            }
    };
    
    
    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 '\\n'");
            for (int i = 0; i < iters; ++i)
                std::cout << '\n';
        }
        {
            TimedSection s("printf with only '\\n'");
            for (int i = 0; i < iters; ++i)
                printf("\n");
        }
        {
            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 '\\n'");
            for (int i = 0; i < iters; ++i)
                std::cout << "01234567890123456789\n";
        }
        {
            TimedSection s("printf with string constant and '\\n'");
            for (int i = 0; i < iters; ++i)
                printf("01234567890123456789\n");
        }
        {
            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 '\\n'");
            for (int i = 0; i < iters; ++i)
                std::cout << text << "01234567890123456789" << i << '\n';
        }
        {
            TimedSection s("printf with some stuff and '\\n'");
            for (int i = 0; i < iters; ++i)
                printf("%s01234567890123456789%i\n", 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 << '\n';
        }
        {
            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 << '\n';
                }
        }
        {
            TimedSection s("printf with formatted double");
            for (int i = 0; i < iters; ++i)
                printf("%8.3f%i\n", 8.315, i);
        }
    }
    

    The result is:

    cout with only endl    6453.000000 ms
    cout with only '\n'    125.000000 ms
    printf with only '\n'    156.000000 ms
    cout with string constant and endl    6937.000000 ms
    cout with string constant and '\n'    1391.000000 ms
    printf with string constant and '\n'    3391.000000 ms
    cout with some stuff and endl    9672.000000 ms
    cout with some stuff and '\n'    7296.000000 ms
    printf with some stuff and '\n'    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
    
    0 讨论(0)
  • 2020-11-22 07:21

    People often claim that printf is much faster. This is largely a myth. I just tested it, with the following results:

    cout with only endl                     1461.310252 ms
    cout with only '\n'                      343.080217 ms
    printf with only '\n'                     90.295948 ms
    cout with string constant and endl      1892.975381 ms
    cout with string constant and '\n'       416.123446 ms
    printf with string constant and '\n'     472.073070 ms
    cout with some stuff and endl           3496.489748 ms
    cout with some stuff and '\n'           2638.272046 ms
    printf with some stuff and '\n'         2520.318314 ms
    

    Conclusion: if you want only newlines, use printf; otherwise, cout is almost as fast, or even faster. More details can be found on my blog.

    To be clear, I'm not trying to say that iostreams are always better than printf; I'm just trying to say that you should make an informed decision based on real data, not a wild guess based on some common, misleading assumption.

    Update: Here's the full code I used for testing. Compiled with g++ without any additional options (apart from -lrt for the timing).

    #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\n"; 
            }
    };
    
    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 '\\n'");
            for (int i = 0; i < iters; ++i)
                std::cout << '\n';
        }
        {
            TimedSection s("printf with only '\\n'");
            for (int i = 0; i < iters; ++i)
                printf("\n");
        }
        {
            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 '\\n'");
            for (int i = 0; i < iters; ++i)
                std::cout << "01234567890123456789\n";
        }
        {
            TimedSection s("printf with string constant and '\\n'");
            for (int i = 0; i < iters; ++i)
                printf("01234567890123456789\n");
        }
        {
            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 '\\n'");
            for (int i = 0; i < iters; ++i)
                std::cout << text << "01234567890123456789" << i << '\n';
        }
        {
            TimedSection s("printf with some stuff and '\\n'");
            for (int i = 0; i < iters; ++i)
                printf("%s01234567890123456789%i\n", text, i);
        }
    }
    
    0 讨论(0)
  • 2020-11-22 07:23
    cout<< "Hello";
    printf("%s", "Hello"); 
    

    Both are used to print values. They have completely different syntax. C++ has both, C only has printf.

    0 讨论(0)
  • 2020-11-22 07:24

    Two points not otherwise mentioned here that I find significant:

    1) cout carries a lot of baggage if you're not already using the STL. It adds over twice as much code to your object file as printf. This is also true for string, and this is the major reason I tend to use my own string library.

    2) cout uses overloaded << operators, which I find unfortunate. This can add confusion if you're also using the << operator for its intended purpose (shift left). I personally don't like to overload operators for purposes tangential to their intended use.

    Bottom line: I'll use cout (and string) if I'm already using the STL. Otherwise, I tend to avoid it.

    0 讨论(0)
  • 2020-11-22 07:25

    For me, the real differences which would make me go for 'cout' rather than 'printf' are:

    1) << operator can be overloaded for my classes.

    2) Output stream for cout can be easily changed to a file : (: copy paste :)

    #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) I find cout more readable, especially when we have many parameters.

    One problem with cout is the formatting options. Formatting the data (precision, justificaton, etc.) in printf is easier.

    0 讨论(0)
  • 2020-11-22 07:26

    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);
         ^
    
    0 讨论(0)
提交回复
热议问题