web-dev-qa-db-fra.com

Pourquoi l'allocation initiale de C ++ est-elle tellement plus importante que celle de C?

Lorsque vous utilisez le même code, le simple changement du compilateur (d'un compilateur C à un compilateur C++) modifiera la quantité de mémoire allouée. Je ne sais pas trop pourquoi c'est et j'aimerais mieux le comprendre. Jusqu'à présent, la meilleure réponse que j'ai obtenue est "probablement les flux d'E/S", ce qui n'est pas très descriptif et me fait me poser des questions sur l'aspect "vous ne payez pas pour ce que vous n'utilisez pas" de C++.

J'utilise les compilateurs Clang et GCC, versions 7.0.1-8 et 8.3.0-6 respectivement. Mon système fonctionne sur Debian 10 (Buster), le plus récent. Les benchmarks se font via Valgrind Massif.

#include <stdio.h>

int main() {
    printf("Hello, world!\n");
    return 0;
}

Le code utilisé ne change pas, mais que je compile en C ou en C++, il modifie les résultats du benchmark Valgrind. Les valeurs restent cependant cohérentes d'un compilateur à l'autre. Les allocations d'exécution (crête) pour le programme se présentent comme suit:

  • GCC (C): 1 032 octets (1 Ko)
  • G ++ (C++): 73 744 octets, (~ 74 Ko)
  • Clang (C): 1 032 octets (1 Ko)
  • Clang ++ (C++): 73 744 octets (~ 74 Ko)

Pour la compilation, j'utilise les commandes suivantes:

clang -O3 -o c-clang ./main.c
gcc -O3 -o c-gcc ./main.c
clang++ -O3 -o cpp-clang ./main.cpp
g++ -O3 -o cpp-gcc ./main.cpp

Pour Valgrind, je lance valgrind --tool=massif --massif-out-file=m_compiler_lang ./compiler-lang sur chaque compilateur et langue, puis ms_print pour afficher les pics.

Est-ce que je fais quelque chose de mal ici?

137
Rerumu

L'utilisation du tas provient de la bibliothèque standard C++. Il alloue de la mémoire pour une utilisation en bibliothèque interne au démarrage. Si vous ne le liez pas, il ne devrait y avoir aucune différence entre les versions C et C++. Avec GCC et Clang, vous pouvez compiler le fichier avec:

 g ++ -Wl, - selon les besoins main.cpp 

Cela indiquera à l'éditeur de liens de ne pas établir de lien avec les bibliothèques inutilisées. Dans votre exemple de code, la bibliothèque C++ n'est pas utilisée, elle ne doit donc pas être liée à la bibliothèque standard C++.

Vous pouvez également tester cela avec le fichier C. Si vous compilez avec:

 gcc main.c -lstdc ++ 

L'utilisation du tas réapparaîtra, même si vous avez créé un programme C.

L'utilisation du tas dépend évidemment de l'implémentation de la bibliothèque C++ spécifique que vous utilisez. Dans votre cas, il s'agit de la bibliothèque GNU C++, libstdc ++ . D'autres implémentations peuvent ne pas allouer la même quantité de mémoire ou ne pas allouer de mémoire du tout (à du moins pas au démarrage.) La bibliothèque LLVM C++ ( libc ++ ) par exemple ne fait pas d'allocation de tas au démarrage, du moins sur ma machine Linux:

 clang ++ -stdlib = libc ++ main.cpp 

L'utilisation du tas équivaut à ne pas établir de lien du tout avec lui.

(Si la compilation échoue, alors libc ++ n'est probablement pas installé. Le nom du package contient généralement "libc ++" ou "libcxx".)

148
Nikos C.

Ni GCC ni Clang ne sont des compilateurs - ce sont en fait des programmes de pilotes de chaîne d'outils. Cela signifie qu'ils invoquent le compilateur, l'assembleur et l'éditeur de liens.

Si vous compilez votre code avec un compilateur C ou C++, vous obtiendrez le même assemblage. L'assembleur produira les mêmes objets. La différence est que le pilote de la chaîne d'outils fournira des entrées différentes à l'éditeur de liens pour les deux langages différents: différents démarrages (C++ nécessite du code pour exécuter des constructeurs et des destructeurs pour les objets avec une durée de stockage statique ou locale au niveau de l'espace de noms, et nécessite une infrastructure pour la pile des cadres pour prendre en charge le déroulement lors du traitement des exceptions, par exemple), la bibliothèque standard C++ (qui contient également des objets de durée de stockage statique au niveau de l'espace de noms) et probablement des bibliothèques d'exécution supplémentaires (par exemple, libgcc avec son infrastructure de déroulement de pile).

En bref, ce n'est pas le compilateur qui provoque l'augmentation de l'empreinte, c'est la liaison de choses que vous avez choisi d'utiliser en choisissant le langage C++.

Il est vrai que C++ a la philosophie "ne payez que pour ce que vous utilisez", mais en utilisant le langage, vous payez pour cela. Vous pouvez désactiver des parties du langage (RTTI, gestion des exceptions) mais vous n'utilisez plus C++. Comme mentionné dans une autre réponse, si vous n'utilisez pas du tout la bibliothèque standard, vous pouvez demander au pilote de laisser cela de côté (--Wl, - au besoin) mais si vous n'utilisez aucune des fonctionnalités du C++ ou de sa bibliothèque, pourquoi choisissez-vous même le C++ comme langage de programmation?

15
Stephen M. Webb