web-dev-qa-db-fra.com

Définir une variable et son équivalent statique dans la même fonction

Je ne comprends pas comment fonctionne le code suivant:

#include "stdio.h"

int main(void) {
  int i = 3;
  while(i--) {
    static int i = 100;
    i--,
    printf("%d\n", i);
  }
  return 0;
}

Le code compilé avec Clang ou GCC imprime la sortie suivante:

99
98
97

Quelqu'un peut-il m'expliquer ce qui se passe ici? Il semble que deux opérations soient réalisées en une seule instruction et plusieurs fois. Est-ce un comportement indéfini? J'observe le même comportement en C++.

17
Xatyrian

Ce n'est pas un comportement indéfini.

#include "stdio.h"

int main(void) {
  int i = 3; //first i
  while(i--) {
    static int i = 100; //second i
    i--,
    printf("%d\n", i);
  }
  return 0;
}

Dans le corps de la boucle while, on préfère i (deuxième i). Lors de la vérification de l'état d'une boucle, il ne sait pas ce qu'il y a dans le corps. Donc, il n'a pas de problème à choisir d'abord i.

24
Pranit Kothari

Wikipedia dit une chose très importante à ce sujet:

En programmation informatique, la variable shadowing se produit lorsqu'une variable déclarée dans une certaine étendue (bloc de décision, méthode ou classe interne) porte le même nom qu'une variable déclarée dans une étendue externe. Au niveau des identifiants (noms plutôt que variables), on parle de masquage de noms. Cette variable externe est dite ombrée par la variable interne, tandis que l'identifiant interne est censé masquer l'identifiant externe. 

Maintenant, ici dans le bloc, il trouve la variable statique et y travaille, mais la condition while décrémente la i qui est celle déclarée en dehors du bloc. La portée étant différente - il n’ya aucun problème à utiliser la valeur correcte de i. C'est du code C légal, mais pas forcément une bonne façon d'écrire. 

En fait, ceci donne gcc -Wshadow progname.c

progname.c: In function 'main':
progname.c:7:20: warning: declaration of 'i' shadows a previous local [-Wshadow]
         static int i=2;
                    ^
progname.c:5:9: warning: shadowed declaration is here [-Wshadow]
     int i=2;
         ^

De la norme § 6.2.1p4

... Si un identificateur désigne deux entités différentes dans le même espace de noms, les étendues peuvent se chevaucher. Si tel est le cas, la portée d'une entité (la portée interne) se terminera strictement avant celle de l'autre entité (la portée externe). Dans la portée interne, l'identifiant désigne l'entité déclarée dans la portée interne; l'entité déclarée dans la portée externe est masquée (et non visible) dans la portée interne.

11
coderredoc

Il est possible de déclarer une même variable nommée dans une portée imbriquée. Le compilateur les voit comme des variables différentes. C'est très déroutant, mais la variable à laquelle vous accédez à chaque fois est celle déclarée dans la portée la plus interne. En dehors de la while c'est le int i = 3; et à l'intérieur c'est le static int i = 100;

#include "stdio.h"

int main(void) {
  int i = 3; // outer i
  while(i--) { // outer i
    static int i = 100; // inner i
    i--, // inner i
    printf("%d\n", i); // inner i
  }
  return 0;
}

S'il s'agissait d'une fonction autre que principale, le second appel produirait 

96
95
94

etc...

2
CIsForCookies