可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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?
回答1:
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.
回答2:
If you ever hope to i18n your program, stay away from iostreams. The problem is that it can be impossible to properly localize your strings if the sentence is composed of multiple fragments as is done with iostream.
Besides the issue of message fragments, you also have an issue of ordering. Consider a report that prints a student's name and their grade point average:
std::cout << name << " has a GPA of " << gpa << std::endl;
When you translate that to another language, the other language's grammar may need you to show the GPA before the name. AFAIK, iostreams has not way to reorder the interpolated values.
If you want the best of both worlds (type safety and being able to i18n), use Boost.Format.
回答3:
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
- 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?
- 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.
回答4:
I use printf because I hate the ugly <<cout<<
syntax.
回答5:
Use boost::format. You get type safety, std::string support, printf like interface, ability to use cout, and lots of other good stuff. You won't go back.
回答6:
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.
回答7:
Streams are the canonical way. Try making this code work with printf
:
template <typename T>
void output(const T& pX)
{
std::cout << pX << std::endl;
}
Good luck.
What I mean is, you can make operators to allow your types to be outputted to ostream
's, and without hassle use it just like any other type. printf
doesn't fit the the generality of C++, or more specifically templates.
There's more than usability. There's also consistency. In all my projects, I have cout (and cerr
and clog
) tee'd to also output to a file. If you use printf
, you skip all of that. Additionally, consistency itself is a good thing; mixing cout
and printf
, while perfectly valid, is ugly.
If you have an object, and you want to make it output-able, the cleanest way to do this is overload operator<<
for that class. How are you going to use printf
then? You're going to end up with code jumbled with cout
's and printf
's.
If you really want formatting, use Boost.Format while maintaining the stream interface. Consistency and formatting.
回答8:
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.
回答9:
On the whole I agree (hate the << syntax especially if you need complex formatting)
But I should point out the safety aspects.
printf("%x",2.0f)
printf("%x %x",2)
printf("%x",2,2)
Probably won't be noticed by the compiler but could crash your app.
回答10:
Use whatever fits your needs and preferences. If you're comfortable with printf then by all means use it. If you're happier with iostreams stick to 'em. Mix and match as best fits your requirements. This is software, after all - there's better ways and worse ways, but seldom is there only ONE way.
Share and enjoy.
回答11:
I do not like printf. Its lack of type-safety makes it dangerous to use, plus the need to remember format specifiers is a pain. The templated operators that smartly do the right thing are much better. So I always use the C++ streams in C++.
Granted, many people prefer printf, for other reasons, enumerated elsewhere.
回答12:
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!
回答13:
Even though the question is rather old, I want to add my two cents.
Printing user-created objects with printf()
This is rather simple if you think about it - you can stringify your type and sent the string to printf:
std::string to_string(const MyClass &x)
{
return to_string(x.first)+" "+to_string(x.second);
}
//...
printf("%s is awesome", to_string(my_object).c_str()); //more or less
A shame there wasn't (there is C++11 to_string()) standarized C++ interface to stringify objects...
printf() pitfall
A single flag - %n
The only one that is an output parameter - it expects pointer to int. It writes number of succesfully written characters to location pointed by this pointer. Skillful use of it can trigger overrun, which is security vulnerability (see printf() format string attack).
回答14:
You can get the best of both worlds with the fmt library which combines safety and extensibility of iostreams with usability and performance of (s)printf
. Example:
std::string = fmt::format("The answer is {}", 42);
The library supports Python-like and printf format string syntax.
Disclaimer: I'm the author of the fmt library.
回答15:
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.
回答16:
C++ streams are overrated, after all they're in fact just classes with an overloaded operator <<
.
I've read many times that streams are the C++ way as printf is the C way, but they are both library features available in C++, so you should use what suits best.
I mostly prefer printf, but I've also used streams, which provide cleaner code and prevent you from having to match % placeholders to arguments.
回答17:
It depends on the situation. Nothing is perfect. I use both. Streams are good for custom types as you can overload the >> operator in ostream. But when it comes to spacing and etc it's better to use printf(). stringstream and like are better than the C style strcat(). So use one that's appropriate for the situation.
回答18:
I have read warnings saying that cout and cerr are unsafe for multithreading. If true, this is a good reason to avoid using them. Note: I use GNU g++ with openMP.
回答19:
streams are preferred in cpp as they adhere to the object oriented paradigm of cpp,
beside being type safe.
printf , on the other hand is more of a functional approach.
only reason for not using printf in cpp code that i can think of is not being object oriented.
its more of a personal choice.