Should I use printf in my C++ code?

2019-01-10 08:07发布

I generally use cout and cerr to write text to the console. However sometimes I find it easier to use the good old printf statement. I use it when I need to format the output.

One example of where I would use this is:

// Lets assume that I'm printing coordinates... 
printf("(%d,%d)\n", x, y);

// To do the same thing as above using cout....
cout << "(" << x << "," << y << ")" << endl;

I know I can format output using cout but I already know how to use the printf. Is there any reason I shouldn't use the printf statement?

19条回答
倾城 Initia
2楼-- · 2019-01-10 08:40

No reason at all. I think it's just some strange ideology that drives people towards using only C++ libraries even though good old C libs are still valid. I'm a C++ guy and I use C functions a lot too. Never had any problems with them.

查看更多
我只想做你的唯一
3楼-- · 2019-01-10 08:40

Use printf. Do not use C++ streams. printf gives you much better control (such as float precision etc.). The code is also usually shorter and more readable.

Google C++ style guide agrees.

Do not use streams, except where required by a logging interface. Use printf-like routines instead.

There are various pros and cons to using streams, but in this case, as in many other cases, consistency trumps the debate. Do not use streams in your code.

查看更多
forever°为你锁心
4楼-- · 2019-01-10 08:40

My students, who learn cin and cout first, then learn printf later, overwhelmingly prefer printf (or more usually fprintf). I myself have found the printf model sufficiently readable that I have ported it to other programming languages. So has Olivier Danvy, who has even made it type-safe.

Provided you have a compiler that is capable of type-checking calls to printf, I see no reason not to use fprintf and friends in C++.

Disclaimer: I am a terrible C++ programmer.

查看更多
相关推荐>>
5楼-- · 2019-01-10 08:41

I often "drop back" to using printf(), but more often snprintf() for easier formatted output. When programming in C++ I use this wrapper I wrote a while back, called like this (to use your example as above): cout << format("(%d,%d)\n", x, y);

Here's the header (stdiomm.h):

#pragma once

#include <cstdarg>
#include <string>

template <typename T>
std::basic_string<T> format(T const *format, ...);

template <typename T>
std::basic_string<T> vformat(T const *format, va_list args);

And the source (stdiomm.cpp):

#include "stdiomm.h"
#include <boost/scoped_array.hpp>
#include <cstdio>

template <>
std::wstring vformat(wchar_t const *format, va_list arguments)
{
#if defined(_WIN32)
    int required(_vscwprintf(format, arguments));
    assert(required >= 0);
    boost::scoped_array<wchar_t> buffer(new wchar_t[required + 1]);
    int written(vswprintf(buffer.get(), required + 1, format, arguments));
    assert(written == required);
    return std::wstring(buffer.get(), written);
#else
#   error "No implementation yet"
#endif
}

template <>
std::string vformat(char const *format, va_list arguments)
{
#if defined(_WIN32)
    int required(_vscprintf(format, arguments));
    assert(required >= 0);
    boost::scoped_array<char> buffer(new char[required + 1]);
    int written(vsnprintf(buffer.get(), required + 1, format, arguments));
    assert(written == required);
    return std::string(buffer.get(), written);
#else
    char *buffer;
    int printed = vasprintf(&buffer, format, arguments);
    assert(printed != -1);
    std::string retval(buffer, printed);
    free(buffer);
    return retval;      
#endif
}

template <typename T>
std::basic_string<T> format(T const *format, ...)
{
    va_list ap;
    va_start(ap, format);
    std::basic_string<T> retval(vformat(format, ap));
    va_end(ap);
    return retval;
}

template std::wstring format(wchar_t const *format, ...);
template std::string format(char const *format, ...);

Update

After reading some of the other answers, I might have to make a switch to boost::format() myself!

查看更多
做个烂人
6楼-- · 2019-01-10 08:41

I almost always use printf for temporary debugging statements. For more permanent code, I prefer the 'c' streams as they are The C++ Way. Although boost::format looks promising and might replace my stream usage (especially for complexly formatted output), probably nothing will replace printf for me for a long time.

查看更多
趁早两清
7楼-- · 2019-01-10 08:41

Adaptability

Any attempt to printf a non-POD results in undefined behaviour:

struct Foo { 
    virtual ~Foo() {}
    operator float() const { return 0.f; }
};

printf ("%f", Foo());

std::string foo;
printf ("%s", foo);

The above printf-calls yield undefined behaviour. Your compiler may warn you indeed, but those warnings are not required by the standards and not possible for format strings only known at runtime.

IO-Streams:

std::cout << Foo();
std::string foo;
std::cout << foo;

Judge yourself.

Extensibility

struct Person {
    string first_name;
    string second_name;
};
std::ostream& operator<< (std::ostream &os, Person const& p) {
    return os << p.first_name << ", " << p.second_name;
}

cout << p;
cout << p;
some_file << p;

C:

// inline everywhere
printf ("%s, %s", p.first_name, p.second_name);
printf ("%s, %s", p.first_name, p.second_name);
fprintf (some_file, "%s, %s", p.first_name, p.second_name);

or:

// re-usable (not common in my experience)
int person_fprint(FILE *f, const Person *p) {
    return fprintf(f, "%s, %s", p->first_name, p->second_name);
}
int person_print(const Person *p) {
    return person_fprint(stdout, p);
}

Person p;
....
person_print(&p);

Note how you have to take care of using the proper call arguments/signatures in C (e.g. person_fprint(stderr, ..., person_fprint(myfile, ...), where in C++, the "FILE-argument" is automatically "derived" from the expression. A more exact equivalent of this derivation is actually more like this:

FILE *fout = stdout;
...
fprintf(fout, "Hello World!\n");
person_fprint(fout, ...);
fprintf(fout, "\n");

I18N

We reuse our Person definition:

cout << boost::format("Hello %1%") % p;
cout << boost::format("Na %1%, sei gegrüßt!") % p;

printf ("Hello %1$s, %2$s", p.first_name.c_str(), p.second_name.c_str()); 
printf ("Na %1$s, %2$s, sei gegrüßt!", 
        p.first_name.c_str(), p.second_name.c_str()); 

Judge yourself.

I find this less relevant as of today (2017). Maybe just a gut feeling, but I18N is not something that is done on a daily basis by your average C or C++ programmer. Plus, it's a pain in the a...natomy anyways.

Performance

  1. Have you measured the actual significance of printf performance? Are your bottleneck applications seriously so lazy that the output of computation results is a bottleneck? Are you sure you need C++ at all?
  2. The dreaded performance penalty is to satisfy those of you who want to use a mix of printf and cout. It is a feature, not a bug!

If you use iostreams consistently, you can

std::ios::sync_with_stdio(false);

and reap equal runtime with a good compiler:

#include <cstdio>
#include <iostream>
#include <ctime>
#include <fstream>

void ios_test (int n) {
    for (int i=0; i<n; ++i) {
        std::cout << "foobarfrob" << i;
    }
}

void c_test (int n) {
    for (int i=0; i<n; ++i) {
        printf ("foobarfrob%d", i);
    }
}


int main () {
    const clock_t a_start = clock();
    ios_test (10024*1024);
    const double a = (clock() - a_start) / double(CLOCKS_PER_SEC);

    const clock_t p_start = clock();
    c_test (10024*1024);
    const double p = (clock() - p_start) / double(CLOCKS_PER_SEC);

    std::ios::sync_with_stdio(false);
    const clock_t b_start = clock();
    ios_test (10024*1024);
    const double b = (clock() - b_start) / double(CLOCKS_PER_SEC);


    std::ofstream res ("RESULTS");
    res << "C ..............: " << p << " sec\n"
        << "C++, sync with C: " << a << " sec\n"
        << "C++, non-sync ..: " << b << " sec\n";
}

Results (g++ -O3 synced-unsynced-printf.cc, ./a.out > /dev/null, cat RESULTS):

C ..............: 1.1 sec
C++, sync with C: 1.76 sec
C++, non-sync ..: 1.01 sec

Judge ... yourself.

No. You won't forbid me my printf.

You can haz a typesafe, I18N friendly printf in C++11, thanks to variadic templates. And you will be able to have them very, very performant using user-defined literals, i.e. it will be possible to write a fully static incarnation.

I have a proof of concept. Back then, support for C++11 was not as mature as it is now, but you get an idea.

Temporal Adaptability

// foo.h
...
struct Frob {
    unsigned int x;
};
...

// alpha.cpp
... printf ("%u", frob.x); ...

// bravo.cpp
... printf ("%u", frob.x); ...

// charlie.cpp
... printf ("%u", frob.x); ...

// delta.cpp
... printf ("%u", frob.x); ...

Later, your data grows so big you must do

// foo.h
...
    unsigned long long x;
...

It is an interesting exercise maintaining that and doing it bug-free. Especially when other, non-coupled projects use foo.h.

Other.

  • Bug Potential: There's a lot of space to commit mistakes with printf, especially when you throw user input bases strings in the mix (think of your I18N team). You must take care to properly escape every such format string, you must be sure to pass the right arguments, etc. etc..

  • IO-Streams make my binary bigger: If this is a more important issue than maintainability, code-quality, reuseability, then (after verifying the issue!) use printf.

查看更多
登录 后发表回答