在大多数情况下,cout 提供了一种更安全、更方便的方法来处理 C++ 中的输出。 printf 提供了更多低级控制,并且在格式化或性能是主要优先事项的特定场景中可能有用。
Nowadays std::print introduced in C++23 is preferred to both, but in case you are looking for differences between those old printing methods, I included a comparison of those.
Origin:
printf(): Part of the C standard library, usable in both C and C++.
cout: Part of the C++ standard library, specific to C++.
Type Safety:
printf(): Relies on format specifiers (%d for integers, %s for strings, etc.) to interpret data types. Incorrect format specifiers can lead to unexpected behavior or crashes. Chances are your C compiler will catch those issues however.
cout: Type-safe. It automatically determines the data type of variables being output and applies the appropriate formatting.
Formatting:
printf(): Offers a wide range of formatting options using format specifiers and flags. It provides more control over the output presentation.
cout: Provides basic formatting options like inserting spaces or newlines. It's generally less verbose for simple output compared to printf.
Object-Oriented Features:
printf(): Not designed for object-oriented programming features of C++.
cout: Integrates seamlessly with C++ objects and can directly output objects using the insertion operator (<<).
Error Handling:
printf(): Limited error handling capabilities. Issues like typos in format strings might not be caught until runtime.
cout: Provides some level of error checking. For instance, attempting to output data of an incompatible type might throw an exception.
Performance:
printf(): Generally considered faster than cout due to its lower-level implementation in the C library.
cout: Might have slight performance overhead due to type checking and potential error handling.
When to Use Which:
For basic output and data type safety, cout is preferred in C++.
If you need more precise control over formatting or performance is a critical concern, printf might be a better choice.
C++23 introduces std::print which offers a hybrid approach with positional arguments and some formatting capabilities.
cout offers a safer and more convenient way to handle output in C++ for most cases. printf provides more low-level control and might be useful in specific scenarios where formatting or performance is a major priority.
[15.1] Why should I use <iostream> instead of the traditional <cstdio>?
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 <iostream>, the type of object being I/O'd is
known statically by the compiler. In
contrast, <cstdio> uses "%" fields to
figure out the types dynamically.
Less error prone: With <iostream>, 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++ <iostream> 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++ <iostream> mechanism is built from real classes
such as std::ostream and std::istream. Unlike <cstdio>'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.
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
#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);
}
}
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);
}
}
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).
One is a function that prints to stdout. The other is an object that provides several member functions and overloads of operator<< that print to stdout. There are many more differences that I could enumerate, but I'm not sure what you are after.
#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 中格式化数据(精度、合理性等)更容易。
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.
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.
I'm not a programmer, but I have been a human factors engineer. I feel a programming language should be easy to learn, understand and use, and this requires that it have a simple and consistent linguistic structure. Although all the languages is symbolic and thus, at its core, arbitrary, there are conventions and following them makes the language easier to learn and use.
There are a vast number of functions in C++ and other languages written as function(parameter), a syntax that was originally used for functional relationships in mathematics in the pre-computer era. printf() follows this syntax and if the writers of C++ wanted to create any logically different method for reading and writing files they could have simply created a different function using a similar syntax.
In Python we of course can print using the also fairly standard object.method syntax, i.e. variablename.print, since variables are objects, but in C++ they are not.
I'm not fond of the cout syntax because the << operator does not follow any rules. It is a method or function, i.e. it takes a parameter and does something to it. However it is written as though it were a mathematical comparison operator. This is a poor approach from a human factors standpoint.
The idiomatic way to print to the console from c++23 is supposed to be with std::print (or std::println to add a newline), which uses {} placeholders in the format string syntax (see below):
printf uses a replacement-based API with placeholders starting with % replaced with formatted arguments:
printf("The answer is %d.\n", answer);
cout or, more generally, ostreams use a concatenation-based API with parts of a formatted message interleaved with arguments:
std::cout << "The answer is " << answer << ".\n";
A replacement-based API with proper synchronization provides atomicity, e.g. when writing from multiple threads different messages written with printf won't interleave while parts of the messages written with cout may interleave. For this reason C++20 introduced std::osyncstream which is a clunky way of achieving atomicity.
With operator overloading formatting can quickly become cumbersome, e.g.
Matthew Wilson, the author of FastFormat, called this "chevron hell".
Extensibility
cout supports formatting of user-defined types through overloading of operator<<. There is no standard way to do the same with printf, although there is a glibc extension which is rarely used in practice.
Safety
printf uses varargs which are inherently unsafe unless you use something like GCC's format attribute which only works with literal format strings. It is a user's responsibility to correctly pass type information via format specifiers. Any mismatch results in an undefined behavior and is a common source of vulnerabilities.
cout/ostreams are type-safe and the user doesn't need to manually handle type information.
Buffering
cout adds another layer of buffering and synchronizes with the underlying C streams by default. This brings significant performance overhead. It is possible to disable this synchronization at the cost of worse interoperability with C and potentially other languages.
Formatting state
In printf formatting is controlled via a format string and decoupled from the stream itself. In cout/ostreams the formatting state is stored in the stream which may negatively affect performance and cause unexpected results. Quoting N4412: Shortcomings of iostreams:
Formatting parameters (such as uppercase/lowercase and radix) are specified by setting flags, which mostly persist for an arbitrary number of subsequent low-level formatting operations, until explicitly changed. This approach inhibits compile-time checks and compile-time choice of formatting, and potentially establishes state shared between threads (which requires synchronization for access).
Locales
printf uses the global C locale.
cout uses the C++ locale associated with the stream.
Both printf and cout use locales by default.
Performance
cout is often slower than printf for reasons mentioned above: extra buffering and synchronization (can be disabled) and stateful API.
Language
printf is a part of the C standard library and can be used in C and C++. cout is a part of the C++ standard library and can only be used in C++.
You can have the best of both worlds by using C++23 std::print. It provides a replacement-based API with positional arguments. It is extensible, type-safe, doesn't introduce extra buffering and makes localized formatting an opt-in.
With primitives, it probably doesn't matter entirely which one you use. I say where it gets usefulness is when you want to output complex objects.
For example, if you have a class,
#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\n", s.a, s.b, s.c);
// output with cout
cout << s << endl;
return 0;
}
Now the above might not seem all that great, but let's suppose you have to output this in multiple places in your code. Not only that, let's say you add a field "int d." With cout, you only have to change it in once place. However, with printf, you'd have to change it in possibly a lot of places and not only that, you have to remind yourself which ones to output.
With that said, with cout, you can reduce a lot of times spent with maintenance of your code and not only that if you re-use the object "Something" in a new application, you don't really have to worry about output.
#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;
}
并对 cout 与 printf 进行一些扩展测试,添加了“double”测试,如果有人想做更多测试(Visual Studio 2008,发布版本)可执行文件):
#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);
}
}
结果是:
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
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
I would like say that extensibility lack of printf is not entirely true:
In C, it is true. But in C, there are no real classes.
In C++, it is possible to overload cast operator, so, overloading a char* operator and using printf like this:
Foo bar;
...;
printf("%s",bar);
can be possible, if Foo overload the good operator. Or if you made a good method. In short, printf is as extensible as cout for me.
Technical argument I can see for C++ streams (in general... not only cout.) are:
Typesafety. (And, by the way, if I want to print a single '\n' I use putchar('\n')... I will not use a nuke-bomb to kill an insect.).
Simpler to learn. (no "complicated" parameters to learn, just to use << and >> operators)
Work natively with std::string (for printf there is std::string::c_str(), but for scanf?)
For printf I see:
Easier, or at least shorter (in term of characters written) complex formatting. Far more readable, for me (matter of taste I guess).
Better control of what the function made (Return how many characters where written and there is the %n formatter: "Nothing printed. The argument must be a pointer to a signed int, where the number of characters written so far is stored." (from printf - C++ Reference)
Better debugging possibilities. For same reason as last argument.
My personal preferences go to printf (and scanf) functions, mainly because I love short lines, and because I don't think type problems on printing text are really hard to avoid.
The only thing I deplore with C-style functions is that std::string is not supported. We have to go through a char* before giving it to printf (with the std::string::c_str() if we want to read, but how to write?)
TL;DR: Always do your own research, in regard of generated machine code size, performance, readability and coding time before trusting random comments online, including this one.
I'm no expert. I just happened to overhear two co-workers talking about how we should avoid using C++ in embedded systems because of performance issues. Well, interesting enough, I did a benchmark based on a real project task.
In said task, we had to write some config to RAM. Something like:
Here's my benchmark programs (Yes, I know OP asked about printf(), not fprintf(). Try to capture the essence and by the way, OP's link points to fprintf() anyway.)
C program:
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\nsugar=%s\nmilk=%s\nmac=%02X:%02X:%02X:%02X:%02X:%02X\n", coffee, sugar, milk, mac[0], mac[1],mac[2],mac[3],mac[4],mac[5]);
fclose(f);
I did my best to polish them before I looped them both 100,000 times. Here are the results:
C program:
real 0m 8.01s
user 0m 2.37s
sys 0m 5.58s
C++ program:
real 0m 6.07s
user 0m 3.18s
sys 0m 2.84s
Object file size:
C - 2,092 bytes
C++ - 3,272 bytes
Conclusion: On my very specific platform, with a very specific processor, running a very specific version of Linux kernel, to run a program which is compiled with a very specific version of GCC, in order to accomplish a very specific task, I would say the C++ approach is more suitable because it runs significantly faster and provide much better readability. On the other hand, C offers small footprint, in my opinion, means nearly nothing because program size is not of our concern.
发布评论
评论(18)
如今 C+ 中引入了
std::print
+23 比两者都更受欢迎,但如果您正在寻找这些旧打印方法之间的差异,我对它们进行了比较。来源:
printf()
:C 标准库的一部分,可用于 C 和 C++。cout
:C++ 标准库的一部分,特定于 C++。类型安全:
printf():
依赖于格式说明符(%d
表示整数,%s
表示字符串,等)来解释数据类型。不正确的格式说明符可能会导致意外行为或崩溃。不过,您的 C 编译器很可能会捕获这些问题。cout
:类型安全。它自动确定输出变量的数据类型并应用适当的格式。格式化:
printf()
:使用格式说明符和标志提供多种格式化选项。它提供了对输出呈现的更多控制。cout
:提供基本的格式选项,例如插入空格或换行符。与printf
相比,对于简单输出来说,它通常不那么冗长。面向对象的功能:
printf()
:不是为 C++ 的面向对象编程功能而设计的。cout
:与C++对象无缝集成,可以使用插入运算符(<<
)直接输出对象。错误处理:
printf()
:有限的错误处理功能。格式字符串中的拼写错误等问题可能要到运行时才能被发现。cout
:提供某种程度的错误检查。例如,尝试输出不兼容类型的数据可能会引发异常。性能:
printf()
:由于其在 C 库中的较低级别实现,通常被认为比 cout 更快。cout
:由于类型检查和潜在的错误处理,可能会产生轻微的性能开销。何时使用 Which:
cout
。printf
可能是更好的选择。std::print
,它提供了一种具有位置参数和一些格式化功能的混合方法。在大多数情况下,
cout
提供了一种更安全、更方便的方法来处理 C++ 中的输出。printf
提供了更多低级控制,并且在格式化或性能是主要优先事项的特定场景中可能有用。Nowadays
std::print
introduced in C++23 is preferred to both, but in case you are looking for differences between those old printing methods, I included a comparison of those.Origin:
printf()
: Part of the C standard library, usable in both C and C++.cout
: Part of the C++ standard library, specific to C++.Type Safety:
printf():
Relies on format specifiers (%d
for integers,%s
for strings, etc.) to interpret data types. Incorrect format specifiers can lead to unexpected behavior or crashes. Chances are your C compiler will catch those issues however.cout
: Type-safe. It automatically determines the data type of variables being output and applies the appropriate formatting.Formatting:
printf()
: Offers a wide range of formatting options using format specifiers and flags. It provides more control over the output presentation.cout
: Provides basic formatting options like inserting spaces or newlines. It's generally less verbose for simple output compared toprintf
.Object-Oriented Features:
printf()
: Not designed for object-oriented programming features of C++.cout
: Integrates seamlessly with C++ objects and can directly output objects using the insertion operator (<<
).Error Handling:
printf()
: Limited error handling capabilities. Issues like typos in format strings might not be caught until runtime.cout
: Provides some level of error checking. For instance, attempting to output data of an incompatible type might throw an exception.Performance:
printf()
: Generally considered faster than cout due to its lower-level implementation in the C library.cout
: Might have slight performance overhead due to type checking and potential error handling.When to Use Which:
cout
is preferred in C++.printf
might be a better choice.std::print
which offers a hybrid approach with positional arguments and some formatting capabilities.cout
offers a safer and more convenient way to handle output in C++ for most cases.printf
provides more low-level control and might be useful in specific scenarios where formatting or performance is a major priority.来自 C++ 常见问题解答:
另一方面,
printf
的速度要快得多,这可能证明在非常特定和有限的情况下优先使用它而不是cout
。始终先进行简介。 (例如,参见http://programming-designs.com/2009/02/c-speed-test-part-2-printf-vs-cout/)From the C++ FAQ:
On the other hand,
printf
is significantly faster, which may justify using it in preference tocout
in very specific and limited cases. Always profile first. (See, for example, http://programming-designs.com/2009/02/c-speed-test-part-2-printf-vs-cout/)人们经常声称 printf 更快。这在很大程度上是一个神话。我刚刚测试了它,结果如下:
结论:如果你只想换行,请使用 printf;否则,
cout
几乎同样快,甚至更快。更多详细信息可以在我的博客上找到。需要明确的是,我并不是想说 iostream 总是比 printf 更好;我只是想说,您应该根据真实数据做出明智的决定,而不是根据一些常见的误导性假设进行疯狂猜测。
更新:这是我用于测试的完整代码。使用
g++
编译,无需任何其他选项(除了用于计时的-lrt
)。People often claim that
printf
is much faster. This is largely a myth. I just tested it, with the following results: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
iostream
s are always better thanprintf
; 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).我引用:
And I quote:
一个是打印到标准输出的函数。另一个是提供多个成员函数和打印到 stdout 的运算符<< 重载的对象。我可以列举更多差异,但我不确定您想要什么。
One is a function that prints to stdout. The other is an object that provides several member functions and overloads of
operator<<
that print to stdout. There are many more differences that I could enumerate, but I'm not sure what you are after.对我来说,让我选择“cout”而不是“printf”的真正区别是:
1)<< 运算符可以为我的类重载。
2) cout 的输出流可以轻松更改为文件:
(: 复制粘贴:)
3) 我发现 cout 更具可读性,特别是当我们有很多参数时。
cout
的一个问题是格式选项。在printf
中格式化数据(精度、合理性等)更容易。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 :)
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.) inprintf
is easier.我发现这里没有另外提及的两点很重要:
1) 如果您尚未使用 STL,则
cout
会带来很多负担。它向目标文件添加的代码是printf
的两倍多。对于string
也是如此,这是我倾向于使用自己的字符串库的主要原因。2)
cout
使用重载的<<
运算符,我觉得这很不幸。如果您还使用<<
运算符来实现其预期目的(左移),这可能会增加混乱。我个人不喜欢为了与其预期用途无关的目的而重载运算符。底线:如果我已经在使用 STL,我将使用
cout
(和string
)。否则,我倾向于避免它。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 asprintf
. This is also true forstring
, 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
(andstring
) if I'm already using the STL. Otherwise, I tend to avoid it.我不是程序员,但我一直是人因工程师。我觉得编程语言应该易于学习、理解和使用,这就要求它具有简单且一致的语言结构。尽管所有语言都是符号性的,因此其核心是任意的,但有一些约定,遵循这些约定可以使语言更容易学习和使用。
C++ 和其他语言中有大量函数被编写为函数(参数),这种语法最初用于前计算机时代数学中的函数关系。 printf() 遵循此语法,如果 C++ 编写者想要创建任何逻辑上不同的方法来读取和写入文件,他们可以简单地使用类似的语法创建不同的函数。
在Python 中,我们当然可以使用相当标准的
object.method
语法进行打印,即variablename.print,因为变量是对象,但在C++ 中它们不是。我不喜欢 cout 语法,因为 <<运营商不遵守任何规则。它是一个方法或函数,即它接受一个参数并对其执行某些操作。然而,它的编写方式就好像它是一个数学比较运算符。从人为因素的角度来看,这是一个糟糕的方法。
I'm not a programmer, but I have been a human factors engineer. I feel a programming language should be easy to learn, understand and use, and this requires that it have a simple and consistent linguistic structure. Although all the languages is symbolic and thus, at its core, arbitrary, there are conventions and following them makes the language easier to learn and use.
There are a vast number of functions in C++ and other languages written as function(parameter), a syntax that was originally used for functional relationships in mathematics in the pre-computer era.
printf()
follows this syntax and if the writers of C++ wanted to create any logically different method for reading and writing files they could have simply created a different function using a similar syntax.In Python we of course can print using the also fairly standard
object.method
syntax, i.e. variablename.print, since variables are objects, but in C++ they are not.I'm not fond of the cout syntax because the << operator does not follow any rules. It is a method or function, i.e. it takes a parameter and does something to it. However it is written as though it were a mathematical comparison operator. This is a poor approach from a human factors standpoint.
更新 2023:
从 c++23 打印到控制台的惯用方法应该是使用
std::print
(或std::println
添加换行符),它在格式字符串语法中使用{}
占位符(见下文):但是目前,编译器对它的支持很差。
同时我们可以使用 {fmt} 库来实现类似的 API。
请注意,C++ 标准实际上(大部分)基于 {fmt} 的子集。
在这里查看更多相关信息: libfmt 和 std:: 之间有什么区别格式?。
有关格式字符串语法的详细信息,请参阅此处:{fmt} 格式字符串语法。
工作示例:
现场演示 - Godbolt
更新 2024:
当前的 gcc (14.1 )和 clang (18.1) 最终支持使用
标头,因此上面的第一个代码片段可以按预期编译和运行:现场演示 - Godbolt
Update 2023:
The idiomatic way to print to the console from c++23 is supposed to be with
std::print
(orstd::println
to add a newline), which uses{}
placeholders in the format string syntax (see below):However as of now, the compilers support for it is very poor.
Meanwhile we can use the {fmt} library for a similar API.
Note that the c++ standard is actually based (mostly) on a subset of {fmt}.
See more about it here: What are the differences between libfmt and std::format?.
For more info about the format string syntax see here: {fmt} Format String Syntax.
Working example:
Live demo - Godbolt
Update 2024:
The current gcc (14.1) and clang (18.1) finally supports using the
<print>
header so the first code snippet above compiles and runs as expected:Live demo - Godbolt
API
printf
使用基于替换的 API,其中以%
开头的占位符替换为格式化参数:cout
或者,更一般地说,ostream 使用基于串联的 API,其中部分格式化消息与参数交错:具有适当同步的基于替换的 API 提供原子性,例如,当从多个线程写入时,用
printf
写入的不同消息不会交错,而用cout
写入的部分消息可能会交错。为此,C++20 引入了std::osyncstream
< /a> 这是实现原子性的笨拙方法。使用运算符重载格式化很快就会变得很麻烦,例如
FastFormat
的作者 Matthew Wilson 称之为 “V 字形地狱”。
可扩展性
cout
通过重载运算符<<
支持用户定义类型的格式化。尽管有 printf 执行相同操作。 html" rel="nofollow noreferrer">glibc 扩展 在实践中很少使用。安全
printf
使用本质上不安全的可变参数,除非您使用类似 GCC 的format
属性 仅适用于文字格式字符串。用户有责任通过格式说明符正确传递类型信息。任何不匹配都会导致未定义的行为,并且是常见的漏洞来源。cout
/ostreams 是类型安全的,用户不需要手动处理类型信息。缓冲
cout
添加另一层缓冲并默认与底层 C 流同步。这带来了显着的性能开销。禁用此同步是可能的,但代价是与 C 和其他语言的互操作性较差。格式化状态
在
printf
中,格式化是通过格式字符串控制的,并与流本身分离。在 cout/ostreams 中,格式化状态存储在流中,这可能会对性能产生负面影响并导致意外结果。引用 N4412:iostream 的缺点:语言环境
printf
使用全局 C 语言环境。cout
使用与流关联的 C++ 区域设置。printf
和cout
默认情况下都使用语言环境。性能
由于上述原因,
cout
通常比printf
慢:额外的缓冲和同步(可以禁用)以及有状态 API。语言
printf
是C标准库的一部分,可以在C和C++中使用。cout
是C++标准库的一部分,只能在C++中使用。使用 C++23
std:: 可以两全其美打印
。它提供了带有位置参数的基于替换的 API。它是可扩展的、类型安全的,不会引入额外的缓冲,并使本地化格式成为一种选择。免责声明:我是 C++23
std::print
的作者。API
printf
uses a replacement-based API with placeholders starting with%
replaced with formatted arguments:cout
or, more generally, ostreams use a concatenation-based API with parts of a formatted message interleaved with arguments:A replacement-based API with proper synchronization provides atomicity, e.g. when writing from multiple threads different messages written with
printf
won't interleave while parts of the messages written withcout
may interleave. For this reason C++20 introducedstd::osyncstream
which is a clunky way of achieving atomicity.With operator overloading formatting can quickly become cumbersome, e.g.
vs
Matthew Wilson, the author of FastFormat, called this "chevron hell".
Extensibility
cout
supports formatting of user-defined types through overloading ofoperator<<
. There is no standard way to do the same withprintf
, although there is a glibc extension which is rarely used in practice.Safety
printf
uses varargs which are inherently unsafe unless you use something like GCC'sformat
attribute which only works with literal format strings. It is a user's responsibility to correctly pass type information via format specifiers. Any mismatch results in an undefined behavior and is a common source of vulnerabilities.cout
/ostreams are type-safe and the user doesn't need to manually handle type information.Buffering
cout
adds another layer of buffering and synchronizes with the underlying C streams by default. This brings significant performance overhead. It is possible to disable this synchronization at the cost of worse interoperability with C and potentially other languages.Formatting state
In
printf
formatting is controlled via a format string and decoupled from the stream itself. Incout
/ostreams the formatting state is stored in the stream which may negatively affect performance and cause unexpected results. Quoting N4412: Shortcomings of iostreams:Locales
printf
uses the global C locale.cout
uses the C++ locale associated with the stream.Both
printf
andcout
use locales by default.Performance
cout
is often slower thanprintf
for reasons mentioned above: extra buffering and synchronization (can be disabled) and stateful API.Language
printf
is a part of the C standard library and can be used in C and C++.cout
is a part of the C++ standard library and can only be used in C++.You can have the best of both worlds by using C++23
std::print
. It provides a replacement-based API with positional arguments. It is extensible, type-safe, doesn't introduce extra buffering and makes localized formatting an opt-in.Disclaimer: I'm the author of C++23
std::print
.我想指出的是,如果您想在 C++ 中使用线程,如果您使用
cout
您可以获得一些有趣的结果。考虑这段代码:
现在,输出全部被打乱了。它也可能产生不同的结果,请尝试执行多次:
您可以使用
printf
来获得正确的结果,也可以使用mutex
。玩得开心!
I'd like to point out that if you want to play with threads in C++, if you use
cout
you can get some interesting results.Consider this code:
Now, the output comes all shuffled. It can yield different results too, try executing several times:
You can use
printf
to get it right, or you can usemutex
.Have fun!
对于原语,使用哪一种可能并不重要。我说它的用处是当你想要输出复杂的对象时。
例如,如果您有一个类,
现在上面的内容可能看起来不太好,但我们假设您必须在代码中的多个位置输出它。不仅如此,假设您添加了一个字段“int d”。使用 cout,您只需更改一次即可。然而,使用 printf,您可能必须在很多地方更改它,不仅如此,您还必须提醒自己要输出哪些内容。
话虽如此,使用 cout,您可以减少维护代码所花费的大量时间,不仅如此,如果您在新应用程序中重复使用对象“Something”,您实际上不必担心输出。
With primitives, it probably doesn't matter entirely which one you use. I say where it gets usefulness is when you want to output complex objects.
For example, if you have a class,
Now the above might not seem all that great, but let's suppose you have to output this in multiple places in your code. Not only that, let's say you add a field "int d." With cout, you only have to change it in once place. However, with printf, you'd have to change it in possibly a lot of places and not only that, you have to remind yourself which ones to output.
With that said, with cout, you can reduce a lot of times spent with maintenance of your code and not only that if you re-use the object "Something" in a new application, you don't really have to worry about output.
两者都用于打印值。它们具有完全不同的语法。 C++两者都有,C
只有 printf 。
Both are used to print values. They have completely different syntax. C++ has both, C
only has printf.
当然,您可以写一些更好的“东西”来保持维护:
并对 cout 与 printf 进行一些扩展测试,添加了“double”测试,如果有人想做更多测试(Visual Studio 2008,发布版本)可执行文件):
结果是:
Of course you can write "something" a bit better to keep maintenance:
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):
The result is:
我想说
printf
缺乏可扩展性并不完全正确:在C语言中,确实如此。但在 C 中,没有真正的类。
在 C++ 中,可以重载强制转换运算符,因此,重载
char*
运算符并使用printf
是可能的:如果 Foo 重载了 good 运算符。或者如果你有一个好的方法。简而言之,对我来说,
printf
与cout
一样可扩展。我可以看到的 C++ 流的技术参数(一般来说......不仅仅是 cout。)是:
类型安全。 (顺便说一句,如果我想打印一个
'\n'
我使用putchar('\n')
...我不会使用核武器-用炸弹杀死昆虫。)。学习起来更简单。 (无需学习“复杂”参数,只需使用
<<
和>>
运算符)与
std::string 本地工作
(对于printf
有std::string::c_str()
,但是对于scanf
?)对于
printf
我看到:更容易,或者至少更短(就写入的字符而言)复杂的格式。 对我来说更具可读性(我猜是品味问题)。
更好地控制函数的内容(返回写入的字符数,并且有
%n
格式化程序:“没有打印任何内容。参数必须是指向有符号 int 的指针,其中数字到目前为止写入的字符已存储。”(来自 printf - C++ 参考)更好的调试可能性。
我个人偏好使用
printf
(和scanf<)。 /code>) 函数,主要是因为我喜欢短行,而且我认为打印文本时的类型问题确实很难避免。
我对 C 风格函数的唯一不满是不支持
std::string
。在将其交给printf
之前,我们必须先检查char*
(如果我们想要的话,可以使用std::string::c_str()
读了,但是怎么写呢?)I would like say that extensibility lack of
printf
is not entirely true:In C, it is true. But in C, there are no real classes.
In C++, it is possible to overload cast operator, so, overloading a
char*
operator and usingprintf
like this:can be possible, if Foo overload the good operator. Or if you made a good method. In short,
printf
is as extensible ascout
for me.Technical argument I can see for C++ streams (in general... not only cout.) are:
Typesafety. (And, by the way, if I want to print a single
'\n'
I useputchar('\n')
... I will not use a nuke-bomb to kill an insect.).Simpler to learn. (no "complicated" parameters to learn, just to use
<<
and>>
operators)Work natively with
std::string
(forprintf
there isstd::string::c_str()
, but forscanf
?)For
printf
I see:Easier, or at least shorter (in term of characters written) complex formatting. Far more readable, for me (matter of taste I guess).
Better control of what the function made (Return how many characters where written and there is the
%n
formatter: "Nothing printed. The argument must be a pointer to a signed int, where the number of characters written so far is stored." (from printf - C++ Reference)Better debugging possibilities. For same reason as last argument.
My personal preferences go to
printf
(andscanf
) functions, mainly because I love short lines, and because I don't think type problems on printing text are really hard to avoid.The only thing I deplore with C-style functions is that
std::string
is not supported. We have to go through achar*
before giving it toprintf
(with thestd::string::c_str()
if we want to read, but how to write?)更多差异:
“printf”返回一个整数值(等于打印的字符数),“cout”不返回任何内容
。
cout << “y =”<< 7;
不是原子的。printf("%s = %d", "y", 7);
是原子的。cout 执行类型检查,printf 则不执行。
没有相当于
"% d"
的 iostreamMore differences:
"printf" returns an integer value (equal to the number of characters printed) and "cout" does not return anything
And.
cout << "y = " << 7;
is not atomic.printf("%s = %d", "y", 7);
is atomic.cout performs typechecking, printf doesn't.
There's no iostream equivalent of
"% d"
TL;DR:始终对生成的机器代码大小、性能、可读性和编码时间进行自己的研究在相信网上的随机评论之前,包括这个。
我不是专家。我只是偶然听到两个同事谈论由于性能问题我们应该如何避免在嵌入式系统中使用 C++。嗯,很有趣,我根据真实的项目任务做了一个基准测试。
在上述任务中,我们必须将一些配置写入 RAM。像这样的东西:
这是我的基准程序(是的,我知道OP问的是printf(),而不是fprintf()。尝试抓住本质,顺便说一句,OP的链接指向fprintf( )无论如何。)
C 程序:
C++ 程序:
在循环它们 100,000 次之前,我尽最大努力完善它们。以下是结果:
C 程序:
C++ 程序:
对象文件大小:
结论: 在我非常特定的平台上,使用非常特定的处理器,运行非常特定的版本< strong>Linux 内核,运行使用特定版本的 GCC 编译的程序,以完成非常特定的任务,我会说C++ 方法更合适,因为它的运行速度明显更快,并且可读性更好。另一方面,在我看来,C 占用空间小几乎没有任何意义,因为程序大小不是我们关心的问题。
记住,YMMV。
TL;DR: Always do your own research, in regard of generated machine code size, performance, readability and coding time before trusting random comments online, including this one.
I'm no expert. I just happened to overhear two co-workers talking about how we should avoid using C++ in embedded systems because of performance issues. Well, interesting enough, I did a benchmark based on a real project task.
In said task, we had to write some config to RAM. Something like:
Here's my benchmark programs (Yes, I know OP asked about printf(), not fprintf(). Try to capture the essence and by the way, OP's link points to fprintf() anyway.)
C program:
C++ program:
I did my best to polish them before I looped them both 100,000 times. Here are the results:
C program:
C++ program:
Object file size:
Conclusion: On my very specific platform, with a very specific processor, running a very specific version of Linux kernel, to run a program which is compiled with a very specific version of GCC, in order to accomplish a very specific task, I would say the C++ approach is more suitable because it runs significantly faster and provide much better readability. On the other hand, C offers small footprint, in my opinion, means nearly nothing because program size is not of our concern.
Remeber, YMMV.
printf
是一个函数,而cout
是一个变量。printf
is a function whereascout
is a variable.