Examinons les exemples suivants du monde Hello en C et C++:
_#include <stdio.h>
int main()
{
printf("Hello world\n");
return 0;
}
_
_#include <iostream>
int main()
{
std::cout<<"Hello world"<<std::endl;
return 0;
}
_
Quand je les compile dans godbolt à Assembly, la taille du code C n’est que de 9 lignes (_gcc -O3
_):
_.LC0:
.string "Hello world"
main:
sub rsp, 8
mov edi, OFFSET FLAT:.LC0
call puts
xor eax, eax
add rsp, 8
ret
_
Mais la taille du code C++ est de 22 lignes (_g++ -O3
_):
_.LC0:
.string "Hello world"
main:
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)
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
_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
_
... qui est beaucoup plus grande.
Il est célèbre qu'en C++, vous payez pour ce que vous mangez. Donc, dans ce cas, que vais-je payer?
Ce que vous payez, c'est d'appeler une bibliothèque lourde (moins lourde qu'une impression sur la console). Vous initialisez un objet ostream
. Il y a des rangements cachés. Ensuite, vous appelez std::endl
qui n'est pas synonyme de \n
. La bibliothèque iostream
vous aide à ajuster de nombreux paramètres et à alourdir la charge du processeur plutôt que du programmeur. C'est ce que vous payez.
Passons en revue le code:
.LC0:
.string "Hello world"
main:
Initialisation d'un objet ostream + 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)
Appelez à nouveau cout
pour imprimer une nouvelle ligne et la rincer
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
Initialisation du stockage statique:
_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
En outre, il est essentiel de faire la distinction entre la langue et la bibliothèque.
BTW, ce n'est qu'une partie de l'histoire. Vous ne savez pas ce qui est écrit dans les fonctions que vous appelez.
Donc, dans ce cas, que vais-je payer?
std::cout
est plus puissant et compliqué que printf
. Il prend en charge des éléments tels que les paramètres régionaux, les indicateurs de mise en forme avec état, etc.
Si vous n'en avez pas besoin, utilisez std::printf
ou std::puts
- ils sont disponibles dans <cstdio>
.
Il est célèbre qu'en C++, vous payez pour ce que vous mangez.
Je tiens également à préciser que C++ ! = La bibliothèque standard C++. La bibliothèque standard est supposée être polyvalente et "assez rapide", mais elle sera souvent plus lente qu'une mise en œuvre spécialisée de ce dont vous avez besoin.
D'autre part, le langage C++ s'efforce de rendre possible l'écriture de code sans payer de coûts cachés supplémentaires inutiles (par exemple, opt-in virtual
, pas de garbage collection).
Vous ne comparez pas C et C++. Vous comparez printf
et std::cout
, qui sont capables de différentes choses (paramètres régionaux, formatage avec état, etc.).
Essayez d'utiliser le code suivant pour la comparaison. Godbolt génère le même assemblage pour les deux fichiers (testé avec gcc 8.2, -O3).
principal 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);
}
}
Vos annonces comparent bien des pommes et des oranges, mais pas pour la raison que suggèrent la plupart des autres réponses.
Voyons ce que votre code fait réellement:
"Hello world\n"
"Hello world"
dans std::cout
std::endl
dans std::cout
Apparemment, votre code C++ fait deux fois plus de travail. Pour une comparaison équitable, nous devrions combiner ceci:
#include <iostream>
int main()
{
std::cout<<"Hello world\n";
return 0;
}
… Et tout à coup votre code d'assemblage pour main
ressemble beaucoup à C:
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
En fait, on peut comparer les codes C et C++ ligne par ligne, et il y a très peu de différences:
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
La seule différence réelle est qu'en C++, nous appelons operator <<
avec deux arguments (std::cout
et la chaîne). Nous pourrions même supprimer cette légère différence en utilisant un équivalent plus proche: fprintf
, qui possède également un premier argument spécifiant le flux.
Cela laisse le code d'assemblage pour _GLOBAL__sub_I_main
, qui est généré pour C++ mais pas C. C'est la seule véritable surcharge visible dans cette liste Assembly (il y a plus, une surcharge invisible pour les deux langues, bien sûr). Ce code effectue une configuration unique de certaines fonctions de bibliothèque standard C++ au début du programme C++.
Mais, comme expliqué dans d’autres réponses, la différence pertinente entre ces deux programmes ne se trouve pas dans la sortie Assembly de la fonction main
, étant donné que tous les efforts lourds s’effectuent dans les coulisses.
Il est célèbre qu'en C++, vous payez pour ce que vous mangez. Donc, dans ce cas, que vais-je payer?
C'est simple. Vous payez pour std::cout
. "Vous ne payez que ce que vous mangez" ne signifie pas "vous obtenez toujours les meilleurs prix". Bien sûr, printf
est moins cher. On peut soutenir que std::cout
est plus sûr et plus polyvalent, son coût plus élevé est donc justifié (il coûte plus cher, mais offre plus de valeur), mais cela passe à côté de l'essentiel. Vous n'utilisez pas printf
, vous utilisez std::cout
, vous payez donc pour utiliser std::cout
. Vous ne payez pas pour utiliser printf
.
Un bon exemple est celui des fonctions virtuelles. Les fonctions virtuelles ont des besoins en termes de coût d'exécution et d'espace, mais uniquement si vous les utilisez . Si vous n'utilisez pas de fonctions virtuelles, vous ne payez rien.
Quelques remarques
Même si le code C++ évalue davantage d'instructions d'assembly, il n'en reste qu'une poignée, et toute surcharge de performances est toujours susceptible d'être dépassée par les opérations d'E/S réelles.
En fait, parfois, c'est même mieux que "en C++, vous payez pour ce que vous mangez". Par exemple, le compilateur peut déduire que l'appel de fonction virtuelle n'est pas nécessaire dans certaines circonstances et le transformer en appel non virtuel. Cela signifie que vous pouvez obtenir des fonctions virtuelles pour gratuitement . N'est-ce pas génial?
La "Liste de montage pour printf" n'est PAS destinée à printf, mais à des options de vente (type d'optimisation du compilateur?); printf est beaucoup plus complexe que met ... n'oubliez pas!
Je vois des réponses valables ici, mais je vais entrer un peu plus dans les détails.
Allez au résumé ci-dessous pour trouver la réponse à votre question principale si vous ne voulez pas parcourir tout ce mur de texte.
Donc, dans ce cas, que vais-je payer?
Vous payez pour l'abstraction . Etre capable d'écrire un code plus simple et plus convivial a un coût. En C++, qui est un langage orienté objet, presque tout est un objet. Lorsque vous utilisez un objet, trois choses principales se passent toujours sous le capot:
init()
). En général, l’allocation de mémoire se passe sous le capot comme première étape de cette étape.Vous ne le voyez pas dans le code, mais chaque fois que vous utilisez un objet, les trois opérations ci-dessus doivent se produire. Si vous deviez tout faire manuellement, le code serait évidemment beaucoup plus long.
Désormais, l’abstraction peut être réalisée efficacement sans ajouter de temps système: les compilateurs et les programmeurs peuvent utiliser des méthodes d’introduction et d'autres techniques pour supprimer les frais généraux d’abstraction, mais ce n’est pas votre cas.
La voici décomposée:
std::ios_base
est initialisée, c'est-à-dire la classe de base pour tout ce qui est lié aux E/S.std::cout
est initialisé.std::__ostream_insert
, qui (comme vous l'avez déjà compris par son nom) est une méthode de std::cout
(essentiellement l'opérateur <<
) qui ajoute une chaîne au flux. .cout::endl
est également transmis à std::__ostream_insert
.__std_dso_handle
est transmis à __cxa_atexit
, une fonction globale chargée de "nettoyer" avant de quitter le programme. __std_dso_handle
lui-même est appelé par cette fonction pour désallouer et détruire les objets globaux restants.Dans le code C, très peu d’étapes se passent:
puts
via le registre edi
.puts
est appelé.Pas d'objets n'importe où, donc pas besoin d'initialiser/détruire quoi que ce soit.
Cela ne signifie cependant pas que vous ne "payez" rien en C . Vous payez toujours pour l'abstraction, ainsi que pour l'initialisation de la bibliothèque standard C et la résolution dynamique de la fonction printf
(ou, en fait, puts
, optimisée par le compilateur puisque vous n'avez besoin d'aucune chaîne de formatage. ) arrive encore sous le capot.
Si vous deviez écrire ce programme en pur assemblage, cela ressemblerait à ceci:
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
Ce qui, fondamentalement, ne fait qu'appeler la write
syscall suivie de la exit
syscall. Maintenant ceci serait le strict minimum pour accomplir la même chose.
C est beaucoup plus nu-osseux , et ne fait que le strict minimum nécessaire, laissant le contrôle total à l'utilisateur, qui est en mesure d'optimiser et de personnaliser fondamentalement tout ce qu'ils veulent. Vous indiquez au processeur de charger une chaîne dans un registre, puis appelez une fonction de bibliothèque pour utiliser cette chaîne. C++ est en revanche beaucoup plus complexe et abstrait . Cela présente un avantage énorme lors de l'écriture de code compliqué, et permet une écriture plus facile et un code plus convivial, mais cela a évidemment un coût. Les performances en C++ présenteront toujours un inconvénient si on les compare à C, car , le C++ offre plus que ce qui est nécessaire pour accomplir de telles tâches de base, ce qui alourdit davantage la charge .
Répondant à votre question principale :
Est-ce que je paie pour ce que je ne mange pas?
Dans ce cas spécifique, yes . Vous ne tirez pas parti de tout ce que le C++ peut offrir de plus que le C, mais c'est simplement parce qu'il n'y a rien dans ce morceau de code aussi simple que le C++ qui puisse vous aider: il est si simple que vous n'avez vraiment pas besoin de C++.
Oh, et encore une chose!
Les avantages du C++ peuvent ne pas sembler évidents au premier abord, puisque vous avez écrit un programme très simple et petit, mais regardez un exemple un peu plus complexe et voyez la différence (les deux programmes font exactement la même chose):
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;
}
J'espère que vous pouvez voir clairement ce que je veux dire ici. Vous remarquerez également qu’en C, vous devez gérer la mémoire à un niveau inférieur en utilisant malloc
et free
, comment vous devez faire plus attention à l’indexation et aux tailles et à la plus grande précision lors de la saisie et de la saisie. impression.
Il y a quelques idées fausses pour commencer. Tout d’abord, le programme C++ ne donne pas 22 instructions, c’est plus comme 22 000 instructions (j’ai tiré ce numéro de mon chapeau, mais c’est à peu près dans la stade de baseball). De plus, le code C ne donne pas 9 instructions non plus. Ce ne sont que ceux que vous voyez.
Après avoir fait beaucoup de choses que vous ne voyez pas, le code C appelle une fonction du CRT (qui est généralement présente, mais pas nécessairement, sous la forme d'une bibliothèque partagée), puis ne ne vérifie pas la valeur de retour ou ne gère pas les erreurs, et échoue. Selon les paramètres du compilateur et de l'optimisation, il n'appelle même pas vraiment printf
mais puts
, ou encore quelque chose de plus primitif.
Vous auriez pu écrire plus ou moins le même programme (à l’exception de certaines fonctions init invisible) en C++ également, si seulement vous appeliez la même fonction de la même manière. Ou, si vous voulez être super-correct, cette même fonction préfixée par std::
.
Le code C++ correspondant n’est en réalité pas du tout la même chose. Bien que tout <iostream>
soit bien connu pour être un gros cochon laid qui ajoute une surcharge énorme aux petits programmes (dans un "vrai" programme, vous ne remarquez pas tellement cela), une interprétation un peu plus juste est: qu'il fait énormément de choses que vous ne voyez pas et qui ne fonctionnent que . Y compris, sans toutefois s'y limiter, le formatage magique de presque toutes les choses au hasard, y compris les formats de nombres et les paramètres régionaux différents, etc., la mise en mémoire tampon et le traitement correct des erreurs. La gestion des erreurs? Eh bien oui, devinez quoi, la sortie d'une chaîne peut en réalité échouer, et contrairement au programme C, le programme C++ ne l'ignorerait pas en silence. Considérant ce que std::ostream
fait sous le capot, et sans que personne ne le sache, il est en fait assez léger. Pas comme si je l'utilisais parce que je déteste la syntaxe du flux avec passion. Mais quand même, c'est assez génial si on considère ce que ça fait.
Mais bien sûr, C++ dans son ensemble est pas aussi efficace que C peut l'être. Cela ne peut pas être aussi efficace car ce n'est pas la même chose et ce n'est pas faire la même chose. Si rien d'autre, C++ génère des exceptions (et du code pour les générer, les gérer ou y échouer) et donne certaines garanties que C ne donne pas. Donc, bien sûr, un programme C++ doit nécessairement être un peu plus grand. Dans l’ensemble, cela n’a aucune importance. Au contraire, pour les programmes réels , j'ai rarement constaté que C++ était plus performant car, pour une raison ou une autre, il semble prêter à des optimisations plus favorables. . Ne me demandez pas pourquoi en particulier, je ne saurais pas.
Si, au lieu de "feu-et-oublier-l'espoir-pour-le-meilleur", vous vous souciez d'écrire du code C qui est correct (c'est-à-dire que vous vérifiez réellement erreurs, et le programme se comporte correctement en présence d’erreurs), la différence est alors marginale, si existante.
Vous payez pour une erreur. Dans les années 80, lorsque les compilateurs n'étaient pas assez efficaces pour vérifier les chaînes de format, la surcharge de l'opérateur était considérée comme un bon moyen de renforcer l'apparence de la sécurité de type pendant io. Cependant, chacune de ses bannières est mal implémentée ou en faillite conceptuelle:
La partie la plus répugnante du flux C++ io api est l’existence de cette bibliothèque d’entêtes de mise en forme. En plus d'être dynamique et laide et sujet aux erreurs, il associe le formatage au flux.
Supposons que vous souhaitiez imprimer une ligne avec un caractère hexadécimal rempli de zéros à 8 chiffres, un entier non signé, suivi d'un espace suivi d'un double avec 3 décimales. Avec <cstdio>
, vous obtenez une chaîne de format concise. Avec <ostream>
, vous devez enregistrer l’ancien état, définir l’alignement sur droite, définir le caractère de remplissage, définir la largeur de remplissage, définir la base sur hexadécimal, afficher l’entier, restaurer l’état enregistré (sinon votre formatage entier polluera votre formatage flottant ), affiche l’espace, définit la notation sur fixed, définit la précision, affiche le double et la nouvelle ligne, puis rétablit l’ancienne mise en forme.
// <cstdio>
std::printf( "%08x %.3lf\n", ival, fval );
// <ostream> & <iomanip>
std::ios old_fmt {nullptr};
old_fmt.copyfmt (std::cout);
std::cout << std::right << std::setfill('0') << std::setw(8) << std::hex << ival;
std::cout.copyfmt (old_fmt);
std::cout << " " << std::fixed << std::setprecision(3) << fval << "\n";
std::cout.copyfmt (old_fmt);
<iostream>
est l'affiche de l'utilisation de la surcharge d'opérateur:
std::cout << 2 << 3 && 0 << 5;
std::cout
est plusieurs fois plus lent printf()
. La fonctionnalité et la dépêche virtuelle qui sévit ont leurs conséquences.
<cstdio>
et <iostream>
sont tous les deux thread-safe, dans la mesure où chaque appel de fonction est atomique. Mais, printf()
fait beaucoup plus par appel. Si vous exécutez le programme suivant avec l'option <cstdio>
, vous ne verrez qu'une ligne de f
. Si vous utilisez <iostream>
sur un ordinateur multicœur, vous verrez probablement autre chose.
// g++ -Wall -Wextra -Wpedantic -pthread -std=c++17 cout.test.cpp
#define USE_STREAM 1
#define REPS 50
#define THREADS 10
#include <thread>
#include <vector>
#if USE_STREAM
#include <iostream>
#else
#include <cstdio>
#endif
void task()
{
for ( int i = 0; i < REPS; ++i )
#if USE_STREAM
std::cout << std::hex << 15 << std::dec;
#else
std::printf ( "%x", 15);
#endif
}
int main()
{
auto threads = std::vector<std::thread> {};
for ( int i = 0; i < THREADS; ++i )
threads.emplace_back(task);
for ( auto & t : threads )
t.join();
#if USE_STREAM
std::cout << "\n<iostream>\n";
#else
std::printf ( "\n<cstdio>\n" );
#endif
}
La réponse à cet exemple est que la plupart des gens exercent la discipline de ne jamais écrire dans un seul descripteur de fichier à partir de plusieurs threads. Eh bien, dans ce cas, vous devrez observer que <iostream>
prendra utilement un verrou sur chaque <<
et sur chaque >>
. Alors que dans <cstdio>
, vous ne verrouillez pas aussi souvent et vous avez même la possibilité de ne pas le verrouiller.
<iostream>
utilise plus de verrous pour obtenir un résultat moins cohérent.
En plus de ce que toutes les autres réponses ont dit,
Il y a aussi le fait que std::endl
est pas identique à '\n'
.
C'est une idée fausse malheureusement commune. std::endl
ne signifie pas "nouvelle ligne",
cela signifie "imprimer une nouvelle ligne et ensuite vider le flux". Flushing n'est pas bon marché!
Ignorant complètement les différences entre printf
et std::cout
pendant un moment, pour être fonctionnellement équivalent à votre exemple C, votre exemple C++ devrait ressembler à ceci:
#include <iostream>
int main()
{
std::cout << "Hello world\n";
return 0;
}
Et voici un exemple de ce que vos exemples devraient être si vous incluez le rinçage.
C
#include <stdio.h>
int main()
{
printf("Hello world\n");
fflush(stdout);
return 0;
}
C++
#include <iostream>
int main()
{
std::cout << "Hello world\n";
std::cout << std::flush;
return 0;
}
Lorsque vous comparez du code, , vous devez toujours faire attention à ce que vous compariez de la même manière et que vous compreniez les implications de ce que fait votre code. Parfois, même les exemples les plus simples sont plus compliqués que certains ne le réalisent.
Bien que les réponses techniques existantes soient correctes, je pense que la question découle finalement de cette idée fausse:
Il est célèbre qu'en C++, vous payez pour ce que vous mangez.
Ceci est juste un discours marketing de la communauté C++. (Pour être juste, il y a un discours marketing dans toutes les communautés linguistiques.) Cela ne veut pas dire quelque chose de concret sur lequel vous pouvez vraiment compter.
"Vous payez pour ce que vous utilisez" est censé signifier qu'une fonctionnalité C++ n'a de frais généraux que si vous utilisez cette fonctionnalité. Mais la définition de "fonctionnalité" n’est pas infiniment granulaire. Vous finirez souvent par activer des fonctionnalités ayant plusieurs aspects, et même si vous n’avez besoin que d’un sous-ensemble de ces aspects, cela n’est souvent ni pratique ni possible. pour la mise en œuvre d'apporter la fonctionnalité en partie.
En général, beaucoup de langues (même si ce n’est pas toutes les langues) s’efforcent d’être efficaces, avec plus ou moins de succès. C++ est quelque part sur la balance, mais sa conception n’a rien de spécial ou de magique qui lui permettrait de réussir parfaitement dans cet objectif.
Les fonctions d'entrée/sortie en C++ sont écrites avec élégance et sont conçues pour être simples à utiliser. À bien des égards, ils constituent une vitrine pour les fonctionnalités orientées objet de C++.
En contrepartie, vous perdez un peu de performances, mais cela est négligeable comparé au temps mis par votre système d'exploitation à gérer les fonctions à un niveau inférieur.
Vous pouvez toujours utiliser les fonctions de style C, car elles font partie de la norme C++, ou peut-être même abandonner la portabilité et utiliser des appels directs vers votre système d'exploitation.
Comme vous l'avez vu dans d'autres réponses, vous payez lorsque vous liez des bibliothèques générales et appelez des constructeurs complexes. Il n'y a pas de question particulière ici, plus un reproche. Je soulignerai certains aspects du monde réel:
Barne avait pour principe de conception de ne jamais laisser l'efficacité être une raison de rester en C plutôt qu'en C++. Cela dit, il faut être prudent pour obtenir ces gains d'efficacité, et des gains d'efficacité occasionnels ont toujours fonctionné, mais n'étaient pas "techniquement" au sens de la spécification C. Par exemple, la disposition des champs de bits n'était pas vraiment spécifiée.
Essayez de regarder à travers ostream. Oh mon dieu, c'est gonflé! Je ne serais pas surpris de trouver un simulateur de vol là-bas. Même la fonction printf () de stdlib utilise habituellement environ 50K. Ce ne sont pas des programmeurs paresseux: la moitié de la taille de printf est liée à des arguments de précision indirecte que la plupart des gens n'utilisent jamais. Presque toutes les bibliothèques de processeurs vraiment contraintes créent leur propre code de sortie au lieu de printf.
L'augmentation de la taille offre généralement une expérience plus contenue et plus flexible. Par analogie, un distributeur automatique vendra une tasse de substance ressemblant à du café pour quelques pièces et la transaction entière prend moins d'une minute. Entrer dans un bon restaurant implique une table, être assis, commander, attendre, prendre une bonne tasse, recevoir une facture, payer avec le choix de formes, ajouter un pourboire et être souhaité une bonne journée en partant. C'est une expérience différente et plus pratique si vous passez chez des amis pour un repas complexe.
Les gens écrivent encore ANSI C, mais rarement K & R C. Mon expérience est que nous le compilons toujours avec un compilateur C++ en utilisant quelques ajustements de configuration pour limiter ce qui est entraîné. Il existe de bons arguments pour d’autres langages: Go supprime la surcharge polymorphe et le préprocesseur fou ; il y a eu de bons arguments en faveur d'un emballage sur le terrain et d'une disposition de la mémoire plus intelligents. IMHO Je pense que toute conception de langage devrait commencer par une liste d'objectifs, un peu comme le Zen of Python .
Ce fut une discussion amusante. Vous vous demandez pourquoi vous ne pouvez pas avoir des bibliothèques magiquement petites, simples, élégantes, complètes et flexibles?
Il n'y a pas de reponse. Il n'y aura pas de réponse. C'est la réponse.