Qu'advient-il déclaré, variable non initialisée en C? A-t-elle une valeur?
Si en C j'écris:
int num;
Avant de m'affecter quoi que ce soit à num
, est la valeur de num
indéterminée?
- Euh, n'est-ce pas une variable définie par l', pas une déclara-t-on? (Je suis désolé si c'est mon C++ qui brille à travers...)
- Pas de. Je peux déclarer une variable sans la définir:
extern int x;
Cependant à définir implique toujours déclarant. Ce n'est pas vrai en C++, avec un membre de classe statique des variables on peut définir sans le déclarer, que la déclaration doit être dans la définition de la classe (pas de déclaration!) et la définition doit être en dehors de la définition de la classe. - ee.hawaii.edu/~tep/EE160/Book/chap14/subsection2.1.1.4.html Ressemble définie signifie que vous devez les initialiser, trop.
Vous devez vous connecter pour publier un commentaire.
Variables statiques (fichier portée et la fonction statique) sont initialisés à zéro:
Non-statique des variables (variables locales) sont indéterminée. Les lire avant d'affecter une valeur à un comportement indéfini.
Dans la pratique, ils ont tendance à avoir une certaine absurde de la valeur de y à l'origine de certains compilateurs peuvent même mettre en spécifique, les valeurs fixées pour le rendre évident quand on regarde dans un débogueur, mais strictement parlant, le compilateur est libre de faire quelque chose à partir de la plante invocation les démons par le biais de vos voies nasales.
Quant à savoir pourquoi c'est un comportement indéfini au lieu de simplement "undefined/valeur arbitraire", il y a un certain nombre d'architectures de PROCESSEUR qui ont le drapeau supplémentaire bits dans la représentation de différents types. Un exemple moderne serait l'Itanium, qui a un "Pas une Chose" bit dans ses registres; bien sûr, le C standard auteurs ont analysé certaines architectures plus anciennes.
En essayant de travailler avec une valeur avec ces drapeau bits peut entraîner un PROCESSEUR exception dans une opération qui vraiment ne devrait pas échouer (par exemple, integer plus, ou de l'affectation à une autre variable). Et si vous y allez et laissez une variable non initialisée, le compilateur peut ramasser quelques aléatoire d'ordures avec ces drapeau bits set - sens de toucher à cette variable non initialisée peut être mortelle.
char
; tous les autres peuvent avoir de piège de représentations. Alternativement car l'accès à la variable non initialisée est U. B. de toute façon - conforme compilateur peut tout simplement faire quelques vérifications et de décider pour signaler le problème.int
peut avoir, c'est le nombre qui produit un résultat impair lorsqu'il est multiplié par deux: blog.frama-c.com/index.php?post/2013/03/13/...unsigned int
). C'est l'argument clé dans la revendication de la lié billet de blog que la lecture à partir de la durée indéterminée de la mémoire est considérée comme un comportement non défini par l'optimisation des compilateurs, , même si l'architecture n'est pas connu pour avoir des valeurs de recouvrement. Je pensais que c'était ce que nous étions en train de parler ici.j*2
résultats dans1
, mais parce queJ*2
est jeté par le compilateur et le conditionnel est effectivement éliminé. Cela ne prouve pas que de la magie motif de bits produit1
lorsqu'il est multiplié par 2, seulement que le comportement est en effet undefined et le compilateur peut faire ce que l'enfer qu'elle aime.int a;
) dans le compilateur que j'utilise (gcc), il dit toujours0
. Chaque fois que j'utilise la référence de aucun variable (&x
ou&a
) avant de l'après queprintf
appel à la même fonction (et la variable non initialisée n'est pas global), il imprime des choses comme32765
ou128339264
. Je suppose que cela pourrait être le compilateur de mettre en “spécifique, les valeurs fixées pour le rendre évident quand on regarde dans un débogueur”, mais c'est assez contre-intuitive lorsque vous êtes nouveau à C et simplement tester un certain nombre de cas.0 si globales ou statiques, pour une durée indéterminée si la classe de stockage est automatique
C a toujours été très précis sur les valeurs initiales des objets. Si global ou
static
, ils seront remis à zéro. Siauto
, la valeur est indéterminée.Ce fut le cas lors de la pré-C89 compilateurs et a été précisé par le K&R et dans DMR original C rapport.
Ce fut le cas en C89, voir la section 6.5.7 Initialisation.
Ce fut le cas en C99, voir la section 6.7.8 Initialisation.
Sur ce qu'est exactement indéterminée moyens, je ne suis pas sûr pour C89, C99 dit:
Mais indépendamment de ce que les normes de dire, dans la vraie vie, chaque page de pile en fait de commencer à zéro, mais quand votre programme ressemble à tout
auto
de stockage de valeurs de classe, il voit tout ce qui a été laissé par votre propre programme à la dernière fois qu'elle a utilisé ces pile d'adresses. Si vous allouer beaucoup deauto
tableaux, vous verrez par la suite commencer soigneusement avec des zéros.Vous pourriez vous demander, pourquoi est-il de cette façon? Une autre SORTE de réponse traite de cette question, voir: https://stackoverflow.com/a/2091505/140740
indeterminate value
peut être trouvé à 3.19.2.Il dépend de la durée de stockage de la variable. Une variable statique de la durée de stockage est toujours implicitement initialisé à zéro.
Automatique (locale) des variables, une variable non initialisée a une valeur indéterminée. Une valeur indéterminée, entre autres choses, dire que quelle que soit la "valeur", vous pouvez "voir" dans cette variable n'est pas seulement imprévisible, il n'est même pas garanti d'être stable. Par exemple, dans la pratique (c'est à dire en ignorant l'UB pour la seconde), ce code
ne garantit pas que les variables
a
etb
recevrez des valeurs identiques. Fait intéressant, ce n'est pas un pédant concept théorique, facilement ce qui se passe dans la pratique en tant que résultat de l'optimisation.Donc, en général, la réponse que "c'est initialisée avec n'importe quelle poubelle était dans la mémoire" n'est pas même à distance correcte. Non initialisée de la variable de comportement est différent de celui d'une variable initialisé avec les ordures ménagères.
Ubuntu 15.10, Noyau 4.2.0, x86-64, GCC 5.2.1 exemple
Assez de normes, regardons une mise en œuvre 🙂
Variable locale
Normes: un comportement indéfini.
Mise en œuvre: le programme alloue de l'espace de pile, et ne se déplace jamais rien à cette adresse, donc tout ce qui était là précédemment est utilisé.
compiler avec:
sorties:
et la décompilation avec:
à:
De notre connaissance de x86-64 conventions d'appel:
%rdi
est le premier argument de printf, donc la chaîne"%d\n"
à l'adresse0x4005e4
%rsi
est le deuxième argument de printf, ainsii
.Il vient de
-0x4(%rbp)
, qui est le premier de 4 octets variable locale.À ce point,
rbp
est dans la première page de la pile a été alloué par le noyau, afin de comprendre que la valeur de nous regarder dans le code du noyau et de trouver ce qui définit.TODO ne le noyau jeu de mémoire à quelque chose avant de le réutiliser pour d'autres processus, lorsqu'un processus meurt? Si non, le nouveau processus serait capable de lire la mémoire des autres programmes, les fuites de données. Voir: Sont les valeurs non initialisées jamais un risque pour la sécurité?
Ensuite, nous pouvons aussi jouer avec notre propre pile de modifications et d'écrire des choses amusantes comme:
Variables globales
Normes: 0
Mise en œuvre:
.bss
section.compile:
# 601044 <i>
dit quei
est à l'adresse0x601044
et:contient:
qui dit
0x601044
est droit dans le milieu de la.bss
, qui commence à0x601040
et est long de 8 octets.La ELFE de la norme garantit ensuite que la section nommée
.bss
est complètement rempli avec des zéros:En outre, le type
SHT_NOBITS
est efficace et n'occupe pas d'espace sur le fichier exécutable:C'est ensuite le noyau Linux à zéro que la région de mémoire lors du chargement du programme en mémoire quand il se met en marche.
Cela dépend. Si cette définition est global (hors fonction) puis
num
seront initialisées à zéro. Si c'est en local (à l'intérieur d'une fonction), alors sa valeur est indéterminée. En théorie, même la tentative de lecture de la valeur a un comportement indéfini -- C permet la possibilité de bits qui ne contribuent pas à la valeur, mais qui doivent être établis dans des façons pour vous d'obtenir les résultats définis à partir de la lecture de la variable.La base réponse est, oui, il n'est pas défini.
Si vous voyez le comportement étrange de ce fait, il peut dépendait de l'endroit où elle est déclarée. Si au sein d'une fonction sur la pile, puis le contenu sera plus que probablement être différent à chaque fois que la fonction est appelée. Si c'est un statique ou d'un module de portée, il n'est pas défini, mais ne changera pas.
Si le stockage de classe est statique ou mondiale, puis pendant le chargement, le BSS initialise de la variable ou de l'emplacement de la mémoire(ML) à 0, sauf si la variable est d'abord affecté de la valeur. Dans le cas de locaux les variables non initialisées le piège de la représentation est affecté à l'emplacement de la mémoire. Donc, si l'un de vos registres contenant de l'information importante est remplacé par le compilateur le programme peut se bloquer.
mais certains compilateurs peuvent avoir des mécanismes pour éviter un tel problème.
Je travaillais avec nec v850 de la série quand j'ai réalisé qu'Il y est de piéger la représentation qui a des motifs de bits qui représentent des valeurs non définies pour les types de données à l'exception de l'omble chevalier. Quand j'ai pris un non initialisée char, je suis un zéro la valeur par défaut en raison de piège de la représentation. Cela peut être utile pour any1 à l'aide de necv850es
Parce que les ordinateurs ont finis à la capacité de stockage automatique des variables généralement lieu dans les éléments de stockage (si registres ou de la RAM) qui ont déjà été utilisés pour certains autres arbitraire but. Si une telle variable est utilisée avant une valeur a été attribuée à lui, que le stockage peut tenir quoi qu'il détenait précédemment, et donc le contenu de la variable sera imprévisible.
Comme un supplément de ride, de nombreux compilateurs peuvent garder de variables dans des registres qui sont plus grandes que les associés des types. Bien qu'un compilateur serait nécessaire pour s'assurer que toute la valeur de ce qui est écrit dans une variable et relire sera tronqué et/ou de signe étendu à sa bonne taille, beaucoup de compilateurs effectuer de telles troncature lorsque les variables sont écrites et s'attendre à ce que il aura été effectuée avant que la variable est en lecture. Sur ces compilateurs, quelque chose comme:
pourrait très bien entraîner
wow()
stocker les valeurs 1234567 dans les registres0 et 1, respectivement, et en appelant
foo()
. Depuisx
n'est pas nécessaire dans"toto", et étant donné que les fonctions sont censé mettre à leur valeur de retour dans
inscrire 0, le compilateur peut allouer inscrire 0 à
q
. Simode
est 1 ou3, inscrire 0 sera chargé avec 2 ou 4, respectivement, mais si c'est une
autre valeur, la fonction peut renvoyer tout ce qui était dans le registre 0 (c'est à dire la
valeur 1234567), même si cette valeur n'est pas dans la gamme de uint16_t.
D'éviter d'exiger des compilateurs de faire du travail supplémentaire pour s'assurer que non initialisée
les variables ne semblent jamais à contenir des valeurs en dehors de leur domaine, et éviter d'avoir besoin d'
pour spécifier une période indéterminée comportements dans trop de détails, que dit la Norme
que l'utilisation de non initialisée automatique des variables est un Comportement Indéfini. Dans
certains cas, les conséquences peuvent être encore plus surprenant qu'une
valeur en dehors de la plage de son type. Par exemple, étant donné:
un compilateur pourrait en déduire que, parce que l'invocation
moo()
avec un mode qui estplus de 3 conduira inévitablement à la programme en invoquant Undefined
Le comportement, le compilateur peut omettre le code qui ne serait pertinent
si
mode
est 4 ou plus, tels que le code qui empêcherait normalementle lancement de bombes nucléaires dans de tels cas. Notez que ni la Norme, ni
moderne compilateur philosophie, serait de soins sur le fait que la valeur de retour
de "hey" est ignoré--l'acte d'essayer de le retourner donne un compilateur
licence illimitée pour générer du code arbitraire.
La Valeur de num seront des ordures de la valeur à partir de la mémoire principale(RAM).
son meilleur si vous initialisez la variable juste après la création.
Aussi loin que je l'avais quitté, il est la plupart du temps dépend du compilateur, mais en général la plupart des cas, la valeur est pré suppose que 0 par le cas de déviation.
J'ai eu la valeur d'ordures en cas de VC++ alors que TC a donné de la valeur à 0.
- Je l'Imprimer comme ci-dessous
0
votre compilateur le plus probable va des mesures pour assurer qu'il obtient de cette valeur (par l'ajout de code pour initialiser les variables de toute façon). Certains compilateurs ce faire, quand le faire "debug" de la compilation, mais le choix de la valeur0
pour ces est une mauvaise idée car il permet de masquer les défauts dans votre code (plus la bonne chose serait de garantir un vraiment rare nombre comme0xBAADF00D
ou quelque chose de similaire). Je pense que la plupart compilateur il suffit de laisser n'importe quelle poubelle qui arrive à occuper la mémoire que la valeur de la variable (c'est à dire. c'est en général pas assemued comme0
).