2
votes

Si je déclare une variable dans une boucle for en C, sera-t-elle créée plusieurs fois ou non?

#include <stdio.h>

int main()
{
    for(int i=0;i<100;i++)
    {
        int count=0;
        printf("%d ",++count);
    }
    return 0;
}

output of the above program  is: 1 1 1 1 1 1..........1
Please take a look at the code above. I declared variable "int count=0" inside the for loop.
With my knowledge, the scope of the variable is within the block, so count variable will be alive up to for loop execution.
"int count=0" is executing 100 times, then it has to create the variable 100 times else it has to give the error (re-declaration of the count variable), but it's not happening like that — what may be the reason?According to output the variable is initializing with zero every time.Please help me to find the reason.
c

11 commentaires

Oui, c'est effectivement une nouvelle variable à chaque itération. Le résultat n'est-il pas celui que vous attendez? Sinon, quel sera le résultat selon vous?


La sortie montre que la variable est initialisée (et créée) à chaque itération. C'est ce qu'exige la norme C. L'opération 'create' est probablement un no-op, mais l'initialisation ne l'est pas. Vous obtiendrez un résultat différent avec static int count = 0; .


Lisez Modern C et consultez cette référence C. Une variable n'est pas créée mais a généralement un emplacement mémoire (dans certains cas, votre compilateur C optimisera cela et stockera la variable dans un registre de processeur ). En savoir plus sur la pile d'appels et la variable automatique


Lisez aussi la documentation de votre compilateur C (par exemple GCC ) et de votre débogueur (par exemple GDB ). Compilez votre code avec tous les avertissements et informations de débogage: avec gcc utilisez gcc -Wall -Wextra -g


Votre variable vit dans un bloc, mais la boucle for est en fait en dehors du bloc et la variable est réinitialisée à chaque itération.


Avec GCC, vous pouvez également demander le code assembleur généré en utilisant gcc -fverbose-asm -O -S foo.c et regarder à l'intérieur du foo.s Avec le débogueur GDB, vous pouvez demander l'adresse de la variable et inspecter son contenu en un point d'arrêt


#include <stdio.h> int main () {int count = 0; pour (int i = 0; i <100; i ++) {count = 0; printf ("% d \ n", ++ décompte); } retourne 0; } Je n'ai trouvé aucune différence entre ce code et la sortie de code ci-dessus


@YugandharD Oui, c'est comme prévu. Dans les deux cas, la variable est mise à 0 au début de chaque boucle. Alors bien sûr, le résultat est le même. La portée de la variable est différente, mais elle est indépendante du fait que vous définissez la variable sur 0 à chaque fois.


Pour moi, cette question n'est pas claire. Vous écrivez: "alors il doit créer la variable 100 fois sinon il doit donner l'erreur (re-déclaration de la variable count), mais ça ne se passe pas comme ça" Qu'est-ce qui ne se passe pas et comment le savez-vous?


la variable n'est allouée qu'une seule fois, et elle peut être au début de la fonction, pas nécessairement au début de la boucle, car c'est juste une question de portée. En revanche, l'initialisation à zéro se fait à chaque tour de boucle. Je suis d'accord que cela peut sembler un mais bizarre car il s'agit de deux instructions différentes, avec une portée différente, dans une seule ligne de code: la création et le réglage.


@ JoëlHecht Le standard C ne dit pas que la variable doit être allouée une seule fois au début de la fonction. La norme C ne se soucie pas tant que la sortie est correcte. Un compilateur est autorisé à "supprimer" le count , c'est-à-dire à ne créer aucune variable du tout,


4 Réponses :


2
votes

Un tel code simple peut être visualisé sur http://www.pythontutor.com/c.html pour une compréhension facile.

Pour répondre à votre question, le count est détruit lorsqu'il sort de sa portée, c'est-à-dire la fermeture } de la boucle. Lors de la prochaine itération, une variable du même nom est créée et initialisée à 0, qui est utilisée par le printf .

Et si compter est votre objectif, imprimez i au lieu de count .


0 commentaires

0
votes

int count=0 s'exécute 100 fois, alors il doit créer la variable 100 fois

Non, il définit le count variables une fois, puis lui attribue la valeur 0 100 fois.

  • La définition d'une variable en C n'implique aucune étape ou code particulier pour la "créer" (contrairement par exemple en C ++, où la simple définition d'une variable peut la construire par défaut). Les définitions de variable associent simplement le nom à une "entité" qui représente la variable en interne, et les définitions sont liées à la portée où elles apparaissent.

  • L'affectation d'une variable est une instruction qui est exécutée pendant le déroulement normal du programme. Il a généralement des "effets observables", sinon le compilateur est autorisé à l'optimiser entièrement.

L'exemple d'OP peut être réécrit sous une forme complètement équivalente comme suit.

for(int i=0;i<100;i++)
{
    int count;  // definition of variable count   - defined once in this {} scope
    count=0;    // assignment of value 0 to count - executed once per iteration, 100 times total

    printf("%d ",++count);
}

3 commentaires

Dire que la variable n'est définie qu'une seule fois impliquerait que la variable count dans votre exemple réécrit conserve sa valeur de l'itération précédente entre le int count; déclaration et le count=0; affectation. Mais c'est incorrect. Le int count; déclare la variable avec une valeur indéterminée pour chaque itération. En réalité, la durée de vie de la variable count ne persiste que jusqu'à la fin de l'itération actuelle.


@IanAbbott La portée est l'instruction composée {...} qui forme le corps de la boucle for , et il y a exactement un count variables défini dans cette portée. La variable est initialisée chaque fois que l'instruction composée est exécutée, mais elle n'est définie qu'une seule fois (et ne doit être définie qu'une seule fois, sinon elle violerait l'équivalent C de ODR). Supposons que vous ayez écrit une boucle vide while while(0) { int x; } , la variable x est toujours définie une fois, même si la boucle n'est jamais exécutée.


C'est assez juste. Il existe une seule définition de la variable, mais chaque itération de la boucle utilise une instance distincte de la variable définie.



1
votes

La norme C décrit le langage C à l'aide d'un modèle abstrait d'ordinateur. Dans ce modèle, le count est créé à chaque fois que le corps de la boucle est exécuté et il est détruit lorsque l'exécution du corps se termine. Par «créé» et «détruit», nous entendons que la mémoire lui est réservée et est libérée, et que l'initialisation est effectuée avec la réservation.

Le standard C n'exige pas que les compilateurs implémentent ce modèle servilement. La plupart des compilateurs alloueront une quantité fixe d'espace de pile au démarrage de la routine, avec un espace pour le count inclus dans ce montant fixe, puis count utilisera ce même espace à chaque itération. Ensuite, si nous regardons le code d'assemblage généré, nous ne verrons aucune réservation ou libération de mémoire; la pile ne sera développée et réduite qu'une seule fois pour l'ensemble de la routine, et non pas agrandie et réduite à chaque itération de boucle.

Ainsi, la réponse est double:

  • Dans le modèle informatique abstrait de Câ € ™, une nouvelle durée de vie de count commence et se termine à chaque itération de boucle.
  • Dans la plupart des implémentations actuelles, la mémoire est réservée une seule fois pour count , bien que les implémentations puissent également allouer et libérer de la mémoire à chaque itération.

Cependant, même si vous savez que votre implémentation C n'alloue de l'espace de pile qu'une seule fois par routine lorsque cela est possible, vous devriez généralement penser aux programmes du modèle C à cet égard. Considère ceci:

for (int i = 0; i < 100; ++i)
{
    extern int  baz(void);
    int a[baz()], b[baz()];

    extern void bar(void *, void *);
    bar(a, b);
}

Dans ce code, le compilateur peut allouer quatre octets d'espace de pile à utiliser à la fois pour count et x , à utiliser pour l'un d'entre eux à la fois. La routine augmenterait la pile une fois, au démarrage, y compris quatre octets à utiliser pour count et x . À chaque itération de la boucle, il utiliserait la mémoire d'abord pour count , puis pour x . Cela nous permet de voir que la mémoire est d'abord réservée pour count , puis libérée, puis réservée pour x , puis libérée, puis cela se répète à chaque itération. Les réservations et les versions se produisent conceptuellement même s'il n'y a pas d'instructions pour agrandir et réduire la pile.

Un autre exemple éclairant est:

for (int i = 0; i < 100; ++i)
{
    int count = 0;
    // Do some things with count.
    float x = 0;
    // Do some things with x.
}

Dans ce cas, le compilateur ne peut pas réserver de mémoire pour a et b démarrage de la routine car il ne sait pas combien de mémoire il aura besoin. Dans chaque itération, il doit appeler baz pour trouver la quantité de mémoire nécessaire pour a et combien pour b , puis il doit leur allouer de l'espace de pile (ou une autre mémoire). De plus, étant donné que les tailles peuvent varier d'une itération à l'autre, il n'est pas possible pour a et b de commencer au même endroit dans chaque itération - l'un d'eux doit bouger pour faire place à l'autre. Donc ce code nous permet de voir qu'un nouveau a et un nouveau b doivent être créés à chaque itération.


0 commentaires

0
votes

Eric a raison. Sous une forme beaucoup plus courte:

En règle générale, l'ordinateur détermine au moment de la compilation la quantité de mémoire nécessaire à une fonction et l'emplacement des variables dans la pile. Il n'y a pas d'allocation / libération individuelle de mémoire de pile.

De plus, lorsque vous avez des variables imbriquées dans {accolades} une fois que l'exécution quitte ce jeu d'accolades, le compilateur est libre de réutiliser cette mémoire pour d'autres variables de la fonction. Il y a deux raisons pour lesquelles je fais cela intentionnellement:

  • Les variables sont importantes mais ne sont nécessaires que pendant une courte période, alors pourquoi rendre les piles plus grandes que nécessaire?
  • Si une variable n'a une valeur saine que pendant une durée limitée et qu'il serait dangereux ou bogué à utiliser en dehors de cette portée, ajoutez des accolades supplémentaires afin qu'une utilisation incorrecte génère une erreur du compilateur.

Exemple:

your_function(int a)
{
   int stack_1;
  { 
     int limited_scope_1[10000];
     do_something(a,limited_scope_1);
  }
  { 
     int limited_scope_2[10000];
     do_something_else(a,limited_scope_2);
  }       
}


0 commentaires