Anorexia. If you get an anorexia diagnosis (known as anorexia nervosa), you're not eating enough food.
Intuitive eating has been gaining traction as a method of eating that tunes into natural signals of hunger and fullness and puts pleasure and satisfaction when eating as the forefront goals.
The difference between the present perfect continuous ("I have been eating") and the present perfect simple ("I have eaten") is that the continuous form of the present perfect focuses on a process, something that has been happening over a recent period of time, while the simple form focuses on the end result of ...
So, in this case, what am I paying for?
std::cout
is more powerful and complicated than printf
. It supports things like locales, stateful formatting flags, and more.
If you don't need those, use std::printf
or std::puts
- they're available in <cstdio>
.
It is famous that in C++ you pay for what you eat.
I also want to make it clear that C++ != The C++ Standard Library. The Standard Library is supposed to be general-purpose and "fast enough", but it will often be slower than a specialized implementation of what you need.
On the other hand, the C++ language strives to make it possible to write code without paying unnecessary extra hidden costs (e.g. opt-in virtual
, no garbage collection).
You are not comparing C and C++. You are comparing printf
and std::cout
, which are capable of different things (locales, stateful formatting, etc).
Try to use the following code for comparison. Godbolt generates the same assembly for both files (tested with gcc 8.2, -O3).
main.c:
#include <stdio.h>
int main()
{
int arr[6] = {1, 2, 3, 4, 5, 6};
for (int i = 0; i < 6; ++i)
{
printf("%d\n", arr[i]);
}
return 0;
}
main.cpp:
#include <array>
#include <cstdio>
int main()
{
std::array<int, 6> arr {1, 2, 3, 4, 5, 6};
for (auto x : arr)
{
std::printf("%d\n", x);
}
}
Your listings are indeed comparing apples and oranges, but not for the reason implied in most other answers.
Let’s check what your code actually does:
"Hello world\n"
"Hello world"
into std::cout
std::endl
manipulator into std::cout
Apparently your C++ code is doing twice as much work. For a fair comparison we should combine this:
#include <iostream>
int main()
{
std::cout<<"Hello world\n";
return 0;
}
… and suddenly your assembly code for main
looks very similar to C’s:
main:
sub rsp, 8
mov esi, OFFSET FLAT:.LC0
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
xor eax, eax
add rsp, 8
ret
In fact, we can compare the C and C++ code line by line, and there are very few differences:
sub rsp, 8 sub rsp, 8
mov edi, OFFSET FLAT:.LC0 | mov esi, OFFSET FLAT:.LC0
> mov edi, OFFSET FLAT:_ZSt4cout
call puts | call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
xor eax, eax xor eax, eax
add rsp, 8 add rsp, 8
ret ret
The only real difference is that in C++ we call operator <<
with two arguments (std::cout
and the string). We could remove even that slight difference by using a closer C eqivalent: fprintf
, which also has a first argument specifying the stream.
This leaves the assembly code for _GLOBAL__sub_I_main
, which is generated for C++ but not C. This is the only true overhead that’s visible in this assembly listing (there’s more, invisible overhead for both languages, of course). This code performs a one-time setup of some C++ standard library functions at the start of the C++ program.
But, as explained in other answers, the relevant difference between these two programs won’t be found in the assembly output of the main
function since all the heavy lifting happens behind the scenes.
What you are paying for is to call a heavy library (not as heavy as printing into console). You initialize an ostream
object. There are some hidden storage. Then, you call std::endl
which is not a synonym for \n
. The iostream
library helps you adjusting many settings and putting the burden on the processor rather than the programmer. This is what you are paying for.
Let's review the code:
.LC0:
.string "Hello world"
main:
Initializing an ostream object + cout
sub rsp, 8
mov edx, 11
mov esi, OFFSET FLAT:.LC0
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
Calling cout
again to print a new line and flush
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)
xor eax, eax
add rsp, 8
ret
Static storage initialization:
_GLOBAL__sub_I_main:
sub rsp, 8
mov edi, OFFSET FLAT:_ZStL8__ioinit
call std::ios_base::Init::Init() [complete object constructor]
mov edx, OFFSET FLAT:__dso_handle
mov esi, OFFSET FLAT:_ZStL8__ioinit
mov edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Ev
add rsp, 8
jmp __cxa_atexit
Also, it is essential to distinguish between the language and the library.
BTW, this is just a part of the story. You do not know what is written in the functions you are calling.
It is famous that in C++ you pay for what you eat. So, in this case, what am I paying for?
That's simple. You pay for std::cout
. "You pay for only what you eat" doesn't mean "you always get best prices". Sure, printf
is cheaper. One can argue that std::cout
is safer and more versatile, thus its greater cost is justified (it costs more, but provides more value), but that misses the point. You don't use printf
, you use std::cout
, so you pay for using std::cout
. You don't pay for using printf
.
A good example is virtual functions. Virtual functions have some runtime cost and space requirements - but only if you actually use them. If you don't use virtual functions, you don't pay anything.
A few remarks
Even if C++ code evaluates to more assembly instructions, it's still a handful of instructions, and any performance overhead is still likely dwarfed by actual I/O operations.
Actually, sometimes it's even better than "in C++ you pay for what you eat". For example, compiler can deduce that virtual function call is not needed in some circumstances, and transform that into non-virtual call. That means you may get virtual functions for free. Isn't that great?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With