Let\'s consider the following hello world examples in C and C++:
main.c
#include
int main()
{
printf(\"Hello world\\n\");
r
I see some valid answers here, but I'm going to get a little bit more into the detail.
Jump to the summary below for the answer to your main question if you don't want to go through this entire wall of text.
So, in this case, what am I paying for?
You are paying for abstraction. Being able to write simpler and more human friendly code comes at a cost. In C++, which is an object-oriented language, almost everything is an object. When you use any object, three main things will always happen under the hood:
init()
method). Usually memory allocation happens under the hood as the first thing in this step.You don't see it in the code, but every single time you use an object all of the three above things need to happen somehow. If you were to do everything manually the code would obviously be way longer.
Now, abstraction can be made efficiently without adding overhead: method inlining and other techniques can be used by both compilers and programmers to remove overheads of abstraction, but this is not your case.
Here it is, broken down:
std::ios_base
class is initialized, which is the base class for everything I/O related.std::cout
object is initialized.std::__ostream_insert
, which (as you already figured out by the name) is a method of std::cout
(basically the <<
operator) which adds a string to the stream.cout::endl
is also passed to std::__ostream_insert
.__std_dso_handle
is passed to __cxa_atexit
, which is a global function that is responsible for "cleaning" before exiting the program. __std_dso_handle
itself is called by this function to deallocate and destroy remaining global objects.In the C code, very few steps are happening:
puts
via the edi
register.puts
gets called.No objects anywhere, hence no need to initialize/destroy anything.
This however doesn't mean that you're not "paying" for anything in C. You are still paying for abstraction, and also initialization of the C standard library and dynamic resolution the printf
function (or, actually puts
, which is optimized by the compiler since you don't need any format string) still happen under the hood.
If you were to write this program in pure assembly it would look something like this:
jmp start
msg db "Hello world\n"
start:
mov rdi, 1
mov rsi, offset msg
mov rdx, 11
mov rax, 1 ; write
syscall
xor rdi, rdi
mov rax, 60 ; exit
syscall
Which basically only results in invoking the write
syscall followed by the exit
syscall. Now this would be the bare minimum to accomplish the same thing.
C is way more bare-bone, and only does the bare minimum that is needed, leaving full control to the user, which is able to fully optimize and customize basically anything they want. You tell the processor to load a string in a register and then call a library function to use that string. C++ on the other hand is way more complex and abstract. This has enormous advantage when writing complicated code, and allows for easier to write and more human friendly code, but it obviously comes at a cost. There's always going to be a drawback in performance in C++ if compared to C in cases like this, since C++ offers more than what's needed to accomplish such basic tasks, and thus it adds more overhead.
Answering your main question:
Am I paying for what I am not eating?
In this specific case, yes. You are not taking advantage of anything that C++ has to offer more than C, but that's just because there's nothing in that simple piece of code that C++ could help you with: it is so simple that you really do not need C++ at all.
Oh, and just one more thing!
The advantages of C++ may not look obvious at first glance, since you wrote a very simple and small program, but look at a little bit more complex example and see the difference (both programs do the exact same thing):
C:
#include <stdio.h>
#include <stdlib.h>
int cmp(const void *a, const void *b) {
return *(int*)a - *(int*)b;
}
int main(void) {
int i, n, *arr;
printf("How many integers do you want to input? ");
scanf("%d", &n);
arr = malloc(sizeof(int) * n);
for (i = 0; i < n; i++) {
printf("Index %d: ", i);
scanf("%d", &arr[i]);
}
qsort(arr, n, sizeof(int), cmp)
puts("Here are your numbers, ordered:");
for (i = 0; i < n; i++)
printf("%d\n", arr[i]);
free(arr);
return 0;
}
C++:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main(void) {
int n;
cout << "How many integers do you want to input? ";
cin >> n;
vector<int> vec(n);
for (int i = 0; i < vec.size(); i++) {
cout << "Index " << i << ": ";
cin >> vec[i];
}
sort(vec.begin(), vec.end());
cout << "Here are your numbers:" << endl;
for (int item : vec)
cout << item << endl;
return 0;
}
Hopefully you can clearly see what I mean here. Also notice how in C you have to manage memory at a lower level using malloc
and free
how you need to be more careful about indexing and sizes, and how you need to be very specific when taking input and printing.