Quelle est l'importance d'initialiser une variable

9

Quelle est l'importance d'initialiser les variables?

Une initialisation correcte évite-t-elle les fuites de mémoire ou présente-t-elle des avantages en termes de performances?

Vivek
la source
14
Cela dépend de la langue. Dans certaines langues, il est assez important d'éviter les bogues, dans le reste, c'est simplement une bonne chose à faire pour améliorer la lisibilité.
Telastyn
Merci Telastyn pour votre contribution. Pouvez-vous mettre un cas où cela devient important selon la langue?
Vivek
4
C ++ est le plus connu ici. Dans le débogage, les variables locales sont initialisées à 0 (ou null) par les compilateurs communs, mais sont des ordures aléatoires lors de la compilation pour la publication. (bien que mes connaissances en C ++
remontent
Il s'agit d'une fois brûlé-deux fois timide. Depuis que j'ai vu / eu des bugs causés par des variables non initialisées, en particulier des pointeurs, c'est devenu une habitude. Pour les performances, ce n'est généralement pas pertinent. Pour les fuites de mémoire, ce n'est pas vraiment un problème.
Mike Dunlavey
1
@Telastyn c'est pire que ça. Un comportement indéfini n'est pas limité à l'état de poubelle, tout peut arriver. Le compilateur peut supposer que les chemins qui lisent les variables non initialisées sont inaccessibles et éliminer les effets "non liés" qui se produisent en cours de route.
Caleth

Réponses:

7

Les variables non initialisées rendent un programme non déterministe. Chaque fois que le programme s'exécute, il peut se comporter différemment. Les changements indépendants de l'environnement d'exploitation, de l'heure de la journée, de la phase de la lune et de leurs permutations affectent comment et quand ces démons se manifestent. Le programme peut s'exécuter un million de fois avant la présentation du défaut, il peut le faire à chaque fois ou exécuter un autre million. De nombreux problèmes sont attribués à des "pépins" et ignorés, ou des rapports de défauts de clients fermés comme "non reproductibles". À quelle fréquence avez-vous redémarré une machine pour «résoudre» un problème? Combien de fois avez-vous dit à un client "Je n'ai jamais vu cela se produire, faites-moi savoir si vous le voyez à nouveau" - en espérant (savoir) très bien qu'il ne le fera pas!

Comme la reproduction d'un défaut peut être presque impossible dans l'environnement de test, il est presque impossible de le trouver et de le réparer.

Cela peut prendre des années avant que le bogue n'apparaisse, généralement dans le code considéré comme fiable et stable. Le défaut est supposé être dans un code plus récent - sa recherche peut prendre beaucoup plus de temps. Un changement de compilateur, un commutateur de compilateur, même l'ajout d'une ligne de code peut changer le comportement.

L'initialisation des variables a un énorme avantage en termes de performances, non seulement parce qu'un programme qui fonctionne correctement est infiniment plus rapide que celui qui n'en a pas, mais les développeurs passent moins de temps à rechercher et à corriger les défauts qui ne devraient pas être là et plus de temps à faire un "vrai" travail.

L'autre avantage significatif de l'initialisation des variables est que l'auteur original du code doit décider à quoi les initialiser. Ce n'est pas toujours un exercice trivial, et lorsqu'il n'est pas trivial, il peut être le signe d'une mauvaise conception.

Les fuites de mémoire sont un problème différent, mais une bonne initialisation peut non seulement aider à les prévenir, mais également à les détecter et à trouver la source - sa langue dépend fortement et c'est vraiment une question distincte qui mérite une exploration plus approfondie que celle que je suis en mesure de donner. dans cette réponse.

Edit: Dans certains langages (par exemple C #), il n'est pas possible d'utiliser des variables non initialisées, car le programme ne compilera pas ou ne signalera pas d'erreur lors de son exécution, si cela est fait. Cependant, de nombreux langages avec ces caractéristiques ont des interfaces vers du code potentiellement dangereux, il faut donc faire attention lors de l'utilisation de telles interfaces no pour introduire des variables non initialisées.

mattnz
la source
6
De nombreux langages de programmation définissent automatiquement leurs variables sur une valeur prédéfinie, une grande partie de ce que vous dites ici ne s'applique pas à ces langages.
Robert Harvey
2
Juste pour réitérer ce que @RobertHarvey a dit, rien de tout cela n'est applicable à C #. Il n'y a aucun avantage en termes de performances à initialiser vos variables lorsque vous les déclarez, et il est impossible d'utiliser une variable non initialisée, vous ne pouvez donc pas blâmer les bogues non reproductibles à ce sujet. (Il est possible d'utiliser un champ de classe non initialisé, mais il est défini sur une valeur par défaut et génère un avertissement dans ce cas)
Bobson
4
@mattnz - Le fait est que pour les langages qui se comportent comme C # (ou Java), certains de ces conseils sont trompeurs ou carrément faux. En tant que langue question agnostique, il devrait avoir une langue agnostique réponse, ce qui signifie adressage langues qui font en toute sécurité d' initialiser les variables de la poignée ainsi que ceux qui ne le font pas.
Bobson
1
J'ajouterais également que le problème des variables non initialisées n'est pas difficile à trouver car tout compilateur / analyseur statique à moitié décent en avertira
jk.
1
Pour Java (et C # probablement), l'initialisation prématurée des sections locales n'est pas nécessaire et conduit sans doute à plus de bogues. Par exemple, définir une variable sur null avant de l'attribuer conditionnellement annule la capacité du compilateur à vous dire que l'un des chemins à travers le code peut ne pas entraîner l'attribution de la variable.
JimmyJames
7

L'initialisation d'une variable comme Telastyn l'a souligné peut éviter les bogues. Si la variable est un type de référence, l'initialiser peut éviter des erreurs de référence nulles sur toute la ligne.

Une variable de n'importe quel type qui a une valeur par défaut non nulle prendra de la mémoire pour stocker la valeur par défaut.

Kevin
la source
6

Essayer d'utiliser une variable non initialisée est toujours un bug, il est donc logique de minimiser la probabilité que ce bug se produise.

L'approche la plus courante utilisée par les langages de programmation pour atténuer le problème est d'initialiser automatiquement à une valeur par défaut, donc au moins si vous oubliez d'initialiser une variable, ce sera quelque chose comme 0au lieu de quelque chose comme 0x16615c4b.

Cela résout un grand pourcentage de bogues, si vous aviez de toute façon besoin d'une variable initialisée à zéro. Cependant, l'utilisation d'une variable qui a été initialisée à une valeur incorrecte est tout aussi mauvaise que l'utilisation d'une variable qui n'a pas été initialisée du tout. En fait, cela peut parfois être encore pire, car l'erreur peut être plus subtile et difficile à détecter.

Les langages de programmation fonctionnels résolvent ce problème non seulement en interdisant les valeurs non initialisées, mais en interdisant complètement la réaffectation. Cela élimine le problème et s'avère ne pas être une restriction aussi sévère que vous pourriez le penser. Même dans les langages non fonctionnels, si vous attendez pour déclarer une variable jusqu'à ce que vous ayez une valeur correcte pour l'initialiser avec, votre code a tendance à être beaucoup plus robuste.

En ce qui concerne les performances, c'est probablement négligeable. Au pire, avec des variables non initialisées, vous avez une affectation supplémentaire et bloquez de la mémoire plus longtemps que nécessaire. De bons compilateurs peuvent optimiser les différences dans de nombreux cas.

Les fuites de mémoire sont complètement indépendantes, bien que les variables correctement initialisées aient tendance à être à portée pendant une période de temps plus courte, et peuvent donc être un peu moins susceptibles pour un programmeur de fuir accidentellement.

Karl Bielefeldt
la source
Toujours? Vous voulez dire que "toujours" comme dans "Comment un message Valgrind fixe rendait OpenSSL presque inutile" marc.info/?t=114651088900003&r=1&w=2 ? Ou voulez-vous dire l'autre, le "presque toujours"?
JensG
1
Je peux penser à trois langues qui permettent des variables non initialisées sans erreur, dont l'une les utilise à des fins linguistiques.
DougM
Je serais intéressé par les détails. Je soupçonne que dans ces cas, les variables ne sont pas vraiment non initialisées, mais sont initialisées d'une manière autre que directement par le programmeur sur le site de déclaration. Ou ils sont attribués par des moyens indirects avant d'être déréférencés.
Karl Bielefeldt
5

L'initialisation implique que la valeur initiale est importante. Si la valeur initiale est importante, alors oui, vous devez clairement vous assurer qu'elle est initialisée. Si cela n'a pas d'importance, cela implique qu'il sera initialisé plus tard.

Une initialisation inutile entraîne des cycles CPU gaspillés. Bien que ces cycles gaspillés puissent ne pas avoir d'importance dans certains programmes, dans d'autres programmes, chaque cycle est important car la vitesse est la principale préoccupation. Il est donc très important de comprendre quels sont ses objectifs de performance et si les variables doivent être initialisées ou non.

Les fuites de mémoire sont un problème complètement différent qui implique généralement une fonction d'allocation de mémoire pour émettre et recycler ultérieurement des blocs de mémoire. Pensez à un bureau de poste. Vous allez demander une boîte aux lettres. Ils vous en donnent un. Vous en demandez un autre. Ils vous en donnent un autre. La règle est que lorsque vous avez terminé d'utiliser une boîte aux lettres, vous devez la restituer. Si vous oubliez de le rendre, ils pensent toujours que vous l'avez, et la boîte ne peut être réutilisée par personne d'autre. Il y a donc un morceau de mémoire attaché et non utilisé, et c'est ce qu'on appelle une fuite de mémoire. Si vous continuez à demander des boîtes à un moment donné, vous manquerez de mémoire. J'ai simplifié à l'excès, mais c'est l'idée de base.

Vue elliptique
la source
-1 vous redéfinissez ce que signifie l'initialisation dans ce contexte.
Pieter B
@Pieter B, je ne comprends pas votre commentaire. S'il vous plaît, dites-moi comment je suis, "redéfinissant ce que signifie l'initialisation dans ce contexte". Merci
vue Elliptique
Lisez votre propre phrase, c'est un raisonnement circulaire: "L'initialisation implique que la valeur initiale est importante. Si la valeur initiale est importante, alors oui, vous devez clairement vous assurer qu'elle est initialisée. Si cela n'a pas d'importance, cela implique qu'elle obtiendra initialisé plus tard. "
Pieter B
@Pieter B, Certaines personnes initialisent en règle générale plutôt que pour une raison programmatique, c'est-à-dire qu'elles initialisent si la valeur initiale est importante ou non. N'est-ce pas le cœur d'OQ: à quel point est-il important d'initialiser une variable? Quoi qu'il en soit, vous avez été voté ici.
Vue elliptique du
2

Comme d'autres l'ont dit, cela dépend de la langue. Mais je vais démontrer mes idées Java (et Java efficace) sur l'initialisation des variables. Ceux-ci devraient être utilisables pour de nombreuses autres langues de niveau supérieur.

Constantes et variables de classe

Les variables de classe - marquées avec staticen Java - sont comme des constantes. Ces variables doivent normalement être finales et initialisées directement après la définition à l'aide =ou à partir d'un bloc d'initialisation de classe static { // initialize here }.

Des champs

Comme dans de nombreux langages de niveau supérieur et de script, une valeur par défaut sera automatiquement attribuée aux champs. Pour les nombres, charce sera la valeur zéro. Pour les cordes et autres objets, ce sera le cas null. Il nullest maintenant dangereux et doit être utilisé avec parcimonie. Ces champs doivent donc être définis sur une valeur valide dès que possible. Le constructeur est normalement un endroit parfait pour cela. Pour vous assurer que les variables sont définies pendant le constructeur, et non modifiées par la suite, vous pouvez les marquer avec le finalmot - clé.

Essayez de résister à l'envie de l'utiliser nullcomme une sorte de drapeau ou de valeur spéciale. Il est préférable, par exemple, d'inclure un champ spécifique pour conserver l'état. Un champ avec le nom statequi utilise les valeurs d'une Stateénumération serait un bon choix.

Paramètres de méthode

Étant donné que les modifications des valeurs des paramètres (qu'il s'agisse de références à des objets ou à des types de base tels que des entiers, etc.) ne seront pas vues par l'appelant, les paramètres doivent être marqués comme final. Cela signifie que les valeurs de la variable elle-même ne peuvent pas être modifiées. Notez que la valeur des instances d'objets mutables peut être modifiée, la référence ne peut pas être modifiée pour pointer vers un objet différent ou nullbien.

Variables locales

Les variables locales ne sont pas automatiquement initialisées; ils doivent être initialisés avant que leur valeur puisse être utilisée. Une méthode pour vous assurer que votre variable est initialisée consiste à les initialiser directement à une sorte de valeur par défaut. C'est cependant quelque chose que vous ne devriez pas faire. La plupart du temps, la valeur par défaut n'est pas une valeur attendue.

Il est préférable de définir uniquement la variable précisément là où vous en avez besoin. Si la variable ne doit prendre qu'une seule valeur (ce qui est vrai pour la plupart des variables dans un bon code), vous pouvez marquer la variable final. Cela garantit que la variable locale est affectée exactement une fois, pas zéro fois ou deux fois. Un exemple:

public static doMethod(final int x) {
    final int y; // no assignment yet, it's final so it *must* be assigned
    if (x < 0) {
        y = 0;
    } else if (x > 0) {
        y = x;
    } else {
        // do nothing <- error, y not assigned if x = 0
        // throwing an exception here is acceptable though
    }
}

Notez que de nombreuses langues vous avertiront si une variable reste non initialisée avant utilisation. Consultez les spécifications linguistiques et les forums pour voir si vous ne vous inquiétez pas inutilement.

Maarten Bodewes
la source
1

Il n'y a aucun problème avec la désinitialisation des variables.

Le problème ne se pose que lorsque vous lisez une variable qui n'a pas encore été écrite.

Selon le compilateur et / ou le type de variable, l'initialisation est effectuée au démarrage de l'application. Ou pas.

Il est courant de ne pas compter sur l'initialisation automatique.

mouviciel
la source
0

L'initialisation des variables (implicitement ou explicitement) est cruciale. Ne pas initialiser une variable est toujours une erreur (elles peuvent cependant être initialisées implicitement. Voir ci-dessous). Les complices modernes comme le compilateur C # (à titre d'exemple) traitent cela comme une erreur et ne vous permettent pas d'exécuter le code. Une variable non initialisée est simplement inutile et nuisible. À moins que vous ne créiez un générateur de nombres aléatoires, vous attendez d'un morceau de code qu'il produise un résultat déterministe et reproductible. Cela n'est possible que si vous commencez à travailler avec des variables initialisées.

La question vraiment intéressante est de savoir si une variable est initialisée automatiquement ou si vous devez la faire manuellement. Cela dépend de la langue utilisée. En C # par exemple, les champs, c'est-à-dire les "variables" au niveau de la classe, sont toujours automatiquement initialisés à la valeur par défaut pour ce type de variable default(T). Cette valeur correspond à un motif binaire composé de tous les zéros. Cela fait partie de la spécification du langage et pas seulement un détail technique de l'implémentation du langage. Vous pouvez donc vous y fier en toute sécurité. Il est prudent de ne pas initialiser une variable explicitement si (et seulement si) la spécification de langage indique qu'elle est initialisée implicitement.Si vous souhaitez une autre valeur, vous devez initialiser la variable explicitement. Toutefois; en C #, les variables locales, c'est-à-dire les variables déclarées dans les méthodes, ne sont pas initialisées automatiquement et vous devez toujours initialiser la variable explicitement.

Olivier Jacot-Descombes
la source
2
ce n'est pas une question spécifique à C #.
DougM
@DougM: Je sais. Ce n'est pas une réponse spécifique à C #, je viens de prendre C # comme exemple.
Olivier Jacot-Descombes
Toutes les langues ne nécessitent pas d'initialisation explicite des variables. Votre déclaration "ne pas initialiser est toujours une erreur" est fausse et n'ajoute aucune clarté à la question posée. vous voudrez peut-être réviser votre réponse.
DougM
@DougM: Avez-vous supervisé ma phrase "La question vraiment intéressante est de savoir si une variable est initialisée automatiquement ou si vous devez la faire manuellement."?
Olivier Jacot-Descombes
vous voulez dire celui enterré à mi-chemin au milieu d'un paragraphe? Oui. Vous auriez dû le rendre plus visible et ajouter un qualificatif à votre réclamation "toujours".
DougM