Est-il bon de définir une variable à l'intérieur d'une boucle? [fermé]

15

Mon instructeur m'a dit une fois que je ne devrais pas définir une variable à l'intérieur d'une boucle , mais honnêtement, je ne comprends toujours pas pourquoi.

Quels en sont les inconvénients?

Quelqu'un pourrait-il m'expliquer cela?

user3260672
la source
7
Quel langage de programmation votre instructeur enseignait-il?
Brian
2
Si vous définissez une variable avec un type non primitif dans une boucle, votre programme peut finir par appeler inutilement son constructeur à chaque fois dans la boucle. Si vous n'avez besoin de le définir qu'une fois en dehors de la boucle, faites-le.
Brandin
17
Lorsque vous avez une telle confusion sur ce que dit un instructeur, la meilleure ressource est de demander à l'instructeur. Ils peuvent vous fournir la communication dense en va-et-vient qu'un site de questions / réponses ne peut pas fournir.
1
Duplicata intersite: Différence entre déclarer des variables avant ou en boucle? (et bien sûr de nombreux doublons sur ce site pour une question aussi élémentaire (y compris ceux qui ne concernent que le C ++)).
Peter Mortensen
2
Ces conseils étaient spécifiques à un contexte. Pour des constraisons de style personnel, je préfère déclarer mes variables sauf s'il y a une raison de ne pas le faire (une habitude de la programmation fonctionnelle). Soit je ne les modifierai pas, et l'optimiseur devrait détecter quand ils ne sont pas nécessaires, soit je le ferai et j'ai évité un bug sérieux. Lorsque ces valeurs intermédiaires constantes sont spécifiques à une itération de la boucle, cela signifie les déclarer à l'intérieur de la boucle. Une autre fois, cependant, lorsque vous devez déclarer des variables en dehors de la boucle, c'est lorsque vous vous y référerez en dehors de la boucle; par exemple, les résultats que vous stockez.
Davislor

Réponses:

42

Ce n'est pas un problème de définir une variable dans une boucle. En fait, c'est une bonne pratique, car les identifiants doivent être limités à la plus petite portée possible.

Ce qui est mauvais, c'est d' affecter une variable dans une boucle si vous pouvez tout aussi bien l'affecter une fois avant que la boucle ne s'exécute. Selon la complexité du côté droit de l'affectation, cela pourrait devenir assez coûteux et même dominer le temps d'exécution de la boucle. Si vous écrivez une boucle qui utilise la même valeur calculée dans toutes les itérations, vous devez certainement la calculer au - dessus de la boucle, ce qui est plus important que de minimiser sa portée.

Pour clarifier: tant que compute()renvoie toujours la même valeur, cette

int value = compute();
while (something) {
    doSomething(value);
}

est plus intelligent que cela:

while (something) {
    int value = compute();
    doSomething(value);
}
Kilian Foth
la source
2
Comment définiriez-vous une variable dans la boucle et l'affecteriez-vous avant la boucle?
Masked Man
6
@MaskedMan, je pense que vous vous méprenez. Ce que Kilian voulait dire, c'est que si une variable se voit attribuer la même valeur à chaque itération d'une boucle, par exemple, la même variable de date est définie sur 1/1/1900, la variable doit être déclarée et la valeur doit être affectée avant la boucle.
ps2goat
2
Je ne pense pas qu'il y ait eu un compilateur écrit au cours des vingt dernières années (en dehors d'un cours de compilateur de premier cycle) qui ne comprendrait pas que vous attribuez la même valeur à chaque itération et déplacez cette affectation hors de la boucle.
TMN
14
@tmn: ne laissez jamais un compilateur faire ce que vous pouvez vous-même faire avec une plus grande clarté de code.
Robert Harvey
10
@TMN, pas nécessairement. Cette optimisation n'est possible que si le compilateur peut prouver que le calcul est sans effet secondaire.
Paul Draper
16

Les types complexes ont des constructeurs et des destructeurs non triviaux.

Ceux-ci seront appelés au début et à la fin du corps de la boucle (car il est initialisé et hors de portée). Si l'initialisation est coûteuse car elle doit allouer de la mémoire, cela doit être évité.

Cependant pour les types triviaux ce n'est pas un problème. L'allocation et la désallocation elles-mêmes ajoutent et soustraient simplement une valeur du pointeur de pile. (qui sera optimisé)

monstre à cliquet
la source
merci, exactement la réponse que je cherchais!
gebbissimo
6

Eh bien, ses conseils sont un peu trop simples (c'est un euphémisme).
Le suivre va de la bonne idée à qui s'en soucie et de la mauvaise idée à l' impossible .

  1. Vous devez le suivre chaque fois que la réutilisation est moins chère que la destruction de l'ancien et la création d'un nouveau.

    #include <iostream>
    #include <string>
    
    int main() {
        std::string s; // Don't needlessly free the buffer
        while ((std::cin >> s))
            std::cout << s;
    }
  2. Vous devez l'éviter par style lorsque cela n'a pas d'importance pour les performances.

    #include <stdio.h>
    #include <stdlib.h>
    int f(int, int);
    
    int main() {
        for (int i = 0; i < 100; ++i) {
            int x = rand(); // Declared here so you don't need to hunt it down.
            printf("%d => %d\n", x, f(x-1, x+i));
        }
    }
  3. Vous devriez vraiment l' éviter lorsqu'il a de moins bonnes performances ou une mauvaise sémantique.

    #include <iostream>
    #include <string>
    std::string generate(int);
    
    int main() {
        for(int i = 0; i < 100; ++i) {
            std::string s = generate(i); // Using copy-ellision here
            std::cout << s;
        }
    }
  4. Vous ne pouvez pas le suivre lorsque le type utilisé ne permet ni l'échange, ni l'assignation de déplacement ni l'assignation de copie.

    #include <iostream>
    #include <puzzle>
    
    int main() {
        for (int i = 0; i < 100; ++i) {
            Puzzle x(i); // Puzzle is an immutable class. For whatever reasons.
            std::cout << x;
        }
    }
Déduplicateur
la source
2
Selon votre définition de "dans une boucle", 1 peut être changé en for (std::string s; std::cin >> s;) ...et être toujours "extérieur"
Caleth