Ce que votre professeur a dit était une déclaration oblique sans beaucoup de clarification. Ce n'est PAS que la décrémentation soit plus rapide que l'incrémentation, mais vous pouvez créer une boucle beaucoup plus rapide avec décrémentation qu'avec incrémentation.
Sans en parler longuement, sans avoir besoin d'utiliser un compteur de boucles, etc. - ce qui compte ci-dessous, c'est juste la vitesse et le nombre de boucles (non nul).
Voici comment la plupart des gens implémentent la boucle avec 10 itérations:
int i;
for (i = 0; i < 10; i++)
{
//something here
}
Dans 99% des cas, c'est tout ce dont on peut avoir besoin, mais avec PHP, PYTHON, JavaScript, il y a tout le monde des logiciels critiques (généralement embarqués, OS, jeux, etc.) où les ticks CPU comptent vraiment, alors regardez brièvement le code d'assemblage de:
int i;
for (i = 0; i < 10; i++)
{
//something here
}
après compilation (sans optimisation), la version compilée peut ressembler à ceci (VS2015):
-------- C7 45 B0 00 00 00 00 mov dword ptr [i],0
-------- EB 09 jmp labelB
labelA 8B 45 B0 mov eax,dword ptr [i]
-------- 83 C0 01 add eax,1
-------- 89 45 B0 mov dword ptr [i],eax
labelB 83 7D B0 0A cmp dword ptr [i],0Ah
-------- 7D 02 jge out1
-------- EB EF jmp labelA
out1:
La boucle entière est de 8 instructions (26 octets). Il contient en fait 6 instructions (17 octets) avec 2 branches. Oui, je sais que cela peut être mieux fait (c'est juste un exemple).
Maintenant, considérez cette construction fréquente que vous trouverez souvent écrite par un développeur embarqué:
i = 10;
do
{
//something here
} while (--i);
Il itère également 10 fois (oui, je sais que la valeur i est différente de celle indiquée pour la boucle, mais nous nous soucions du nombre d'itérations ici). Cela peut être compilé dans ceci:
00074EBC C7 45 B0 01 00 00 00 mov dword ptr [i],1
00074EC3 8B 45 B0 mov eax,dword ptr [i]
00074EC6 83 E8 01 sub eax,1
00074EC9 89 45 B0 mov dword ptr [i],eax
00074ECC 75 F5 jne main+0C3h (074EC3h)
5 instructions (18 octets) et une seule branche. En fait, il y a 4 instructions dans la boucle (11 octets).
La meilleure chose est que certains processeurs (compatibles x86 / x64 inclus) ont des instructions qui peuvent décrémenter un registre, comparer ultérieurement le résultat à zéro et effectuer une branche si le résultat est différent de zéro. Pratiquement TOUS les processeurs PC implémentent cette instruction. En l'utilisant, la boucle n'est en fait qu'une instruction (oui un) de 2 octets:
00144ECE B9 0A 00 00 00 mov ecx,0Ah
label:
// something here
00144ED3 E2 FE loop label (0144ED3h) // decrement ecx and jump to label if not zero
Dois-je expliquer ce qui est le plus rapide?
Maintenant, même si un processeur particulier n'implémente pas l'instruction ci-dessus, tout ce dont il a besoin pour l'émuler est un décrément suivi d'un saut conditionnel si le résultat de l'instruction précédente s'avère être zéro.
Donc, indépendamment de certains cas que vous pouvez signaler en commentaire, pourquoi je me trompe, etc. J'insiste sur le fait qu'il est avantageux de faire une boucle vers le bas si vous savez comment, pourquoi et quand.
PS. Oui, je sais que le compilateur sage (avec le niveau d'optimisation approprié) réécrira pour la boucle (avec le compteur de boucle ascendant) en do..tandis que l'équivalent pour les itérations de boucle constante ... (ou le déroulera) ...