La réponse courte est que la plupart des constructions de langage prises en charge par C sont également prises en charge par le microprocesseur de l'ordinateur cible.Par conséquent, le code C compilé se traduit très bien et efficacement dans le langage d'assemblage du microprocesseur, ce qui entraîne un code plus petit et une empreinte plus petite.
La réponse plus longue nécessite un peu de connaissance du langage assembleur. En C, une déclaration comme celle-ci:
int myInt = 10;
se traduirait par quelque chose comme ça dans l'assemblage:
myInt dw 1
mov myInt,10
Comparez cela à quelque chose comme C ++:
MyClass myClass;
myClass.set_myInt(10);
Le code de langage d'assemblage résultant (selon la taille de MyClass ()), pourrait ajouter jusqu'à des centaines de lignes de langage d'assemblage.
Sans réellement créer de programmes en langage assembleur, le C pur est probablement le code le plus «maigre» et le plus «serré» dans lequel vous pouvez créer un programme.
ÉDITER
Compte tenu des commentaires sur ma réponse, j'ai décidé de faire un test, juste pour ma propre raison. J'ai créé un programme appelé "test.c", qui ressemblait à ceci:
#include <stdio.h>
void main()
{
int myInt=10;
printf("%d\n", myInt);
}
J'ai compilé cela jusqu'à l'assemblage en utilisant gcc. J'ai utilisé la ligne de commande suivante pour le compiler:
gcc -S -O2 test.c
Voici le langage d'assemblage résultant:
.file "test.c"
.section .rodata.str1.1,"aMS",@progbits,1
.LC0:
.string "%d\n"
.section .text.unlikely,"ax",@progbits
.LCOLDB1:
.section .text.startup,"ax",@progbits
.LHOTB1:
.p2align 4,,15
.globl main
.type main, @function
main:
.LFB24:
.cfi_startproc
movl $10, %edx
movl $.LC0, %esi
movl $1, %edi
xorl %eax, %eax
jmp __printf_chk
.cfi_endproc
.LFE24:
.size main, .-main
.section .text.unlikely
.LCOLDE1:
.section .text.startup
.LHOTE1:
.ident "GCC: (Ubuntu 4.9.1-16ubuntu6) 4.9.1"
.section .note.GNU-stack,"",@progbits
Je crée ensuite un fichier appelé "test.cpp" qui définit une classe et produit la même chose que "test.c":
#include <iostream>
using namespace std;
class MyClass {
int myVar;
public:
void set_myVar(int);
int get_myVar(void);
};
void MyClass::set_myVar(int val)
{
myVar = val;
}
int MyClass::get_myVar(void)
{
return myVar;
}
int main()
{
MyClass myClass;
myClass.set_myVar(10);
cout << myClass.get_myVar() << endl;
return 0;
}
Je l'ai compilé de la même manière, en utilisant cette commande:
g++ -O2 -S test.cpp
Voici le fichier d'assemblage résultant:
.file "test.cpp"
.section .text.unlikely,"ax",@progbits
.align 2
.LCOLDB0:
.text
.LHOTB0:
.align 2
.p2align 4,,15
.globl _ZN7MyClass9set_myVarEi
.type _ZN7MyClass9set_myVarEi, @function
_ZN7MyClass9set_myVarEi:
.LFB1047:
.cfi_startproc
movl %esi, (%rdi)
ret
.cfi_endproc
.LFE1047:
.size _ZN7MyClass9set_myVarEi, .-_ZN7MyClass9set_myVarEi
.section .text.unlikely
.LCOLDE0:
.text
.LHOTE0:
.section .text.unlikely
.align 2
.LCOLDB1:
.text
.LHOTB1:
.align 2
.p2align 4,,15
.globl _ZN7MyClass9get_myVarEv
.type _ZN7MyClass9get_myVarEv, @function
_ZN7MyClass9get_myVarEv:
.LFB1048:
.cfi_startproc
movl (%rdi), %eax
ret
.cfi_endproc
.LFE1048:
.size _ZN7MyClass9get_myVarEv, .-_ZN7MyClass9get_myVarEv
.section .text.unlikely
.LCOLDE1:
.text
.LHOTE1:
.section .text.unlikely
.LCOLDB2:
.section .text.startup,"ax",@progbits
.LHOTB2:
.p2align 4,,15
.globl main
.type main, @function
main:
.LFB1049:
.cfi_startproc
subq $8, %rsp
.cfi_def_cfa_offset 16
movl $10, %esi
movl $_ZSt4cout, %edi
call _ZNSolsEi
movq %rax, %rdi
call _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
xorl %eax, %eax
addq $8, %rsp
.cfi_def_cfa_offset 8
ret
.cfi_endproc
.LFE1049:
.size main, .-main
.section .text.unlikely
.LCOLDE2:
.section .text.startup
.LHOTE2:
.section .text.unlikely
.LCOLDB3:
.section .text.startup
.LHOTB3:
.p2align 4,,15
.type _GLOBAL__sub_I__ZN7MyClass9set_myVarEi, @function
_GLOBAL__sub_I__ZN7MyClass9set_myVarEi:
.LFB1056:
.cfi_startproc
subq $8, %rsp
.cfi_def_cfa_offset 16
movl $_ZStL8__ioinit, %edi
call _ZNSt8ios_base4InitC1Ev
movl $__dso_handle, %edx
movl $_ZStL8__ioinit, %esi
movl $_ZNSt8ios_base4InitD1Ev, %edi
addq $8, %rsp
.cfi_def_cfa_offset 8
jmp __cxa_atexit
.cfi_endproc
.LFE1056:
.size _GLOBAL__sub_I__ZN7MyClass9set_myVarEi, .-_GLOBAL__sub_I__ZN7MyClass9set_myVarEi
.section .text.unlikely
.LCOLDE3:
.section .text.startup
.LHOTE3:
.section .init_array,"aw"
.align 8
.quad _GLOBAL__sub_I__ZN7MyClass9set_myVarEi
.local _ZStL8__ioinit
.comm _ZStL8__ioinit,1,1
.hidden __dso_handle
.ident "GCC: (Ubuntu 4.9.1-16ubuntu6) 4.9.1"
.section .note.GNU-stack,"",@progbits
Comme vous pouvez le voir clairement, le fichier d'assemblage résultant est beaucoup plus volumineux sur le fichier C ++ que sur le fichier C. Même si vous supprimez tous les autres éléments et comparez simplement le C "principal" au "principal" C ++, il y a beaucoup de choses supplémentaires.