Bl

Blocs en C

{one: "%{count} exercice", many: "%{count} d'exercices", other: "%{count} exercices"}

À propos de Blocs

Un bloc est un ensemble d'instructions et de déclarations regroupées en une seule unité syntaxique. Les blocs sont souvent, mais pas toujours, définis entre accolades. Les blocs peuvent être imbriqués dans des fonctions et dans d'autres blocs.

Dans l'exemple suivant, la variable number est définie dans la fonction main. La variable flag est définie dans le bloc if, lui-même imbriqué dans la fonction main. Les variables définies dans un bloc ne sont valides que dans ce bloc. Par conséquent, tenter de compiler le code produit une erreur de compilation.

#include <stdio.h>

int main() {

  int number = 1;

  if (number == 1) {
      int flag = 1;
      printf("%d", flag);
  }

  // this line generates a compilation error
  // error: 'flag' undeclared (first use in this function)
  printf("%d", flag);
    
}

Le message d'erreur peut être déroutant, car la variable flag est bel et bien utilisée plus haut dans la fonction. Toutefois, flag est définie dans un bloc à l'intérieur de la fonction. Le bloc interne peut « voir » la variable number de la fonction qui l'englobe, mais la fonction externe ne peut pas « voir » la variable flag définie dans son bloc interne. Une fois que l'exécution quitte le bloc, la variable flag est automatiquement détruite et n'est plus disponible pour le programme. La fonction main elle-même voit l'identifiant flag pour la première fois dans la dernière instruction printf. Si l'on supprime la dernière instruction printf, le programme se compilera et affichera 1 à l'exécution. En effet, la variable flag est valide dans le bloc qui contient la première instruction printf.

Depuis C99, les instructions if, for, while et do sur une seule ligne sont elles aussi considérées comme des blocs. Dans l'exemple suivant, bien que i ne soit pas définie entre accolades, elle n'existe que dans le bloc défini par l'instruction for, et n'est pas visible par la fonction main externe.

#include <stdio.h>

int main() {

    for (int i = 0, j = 11; i < j; i+=2)
        printf("%d", i);

    // this line generates a compilation error
    // error: 'i' undeclared (first use in this function)
    printf("%d", i);
    
}

Si l'on supprime la dernière instruction printf, le programme se compilera et affichera 0 2 4 6 8 10 à l'exécution. Certains langages, comme Go et Rust, précisent explicitement que les instructions conditionnelles et les boucles sur une seule ligne constituent un bloc. Ils exigent que ces instructions sur une seule ligne soient malgré tout entourées d'accolades.

Une fonction se comporte un peu comme un bloc. L'identifiant d'une variable définie dans une fonction ne peut pas être vu à l'extérieur de la fonction, tout comme l'identifiant d'une variable définie dans un bloc ne peut pas être vu à l'extérieur du bloc. Deux variables dans deux fonctions peuvent porter le même nom, puisque les deux fonctions ne voient pas l'une dans l'autre, tout comme deux variables dans deux blocs peuvent porter le même nom, puisque les deux blocs ne voient pas l'un dans l'autre. Mais les fonctions et les blocs ne sont pas identiques. Une fonction ne peut pas être définie à l'intérieur d'une autre fonction, alors qu'un bloc peut être imbriqué dans un autre bloc. Et certains identifiants d'un bloc peuvent être vus à l'extérieur du bloc. Dans l'exemple suivant, le code ne se compilera pas

#include <stdio.h>

int main() {

  {
      int i = 0;
      begin:
      printf("i == %d\n", i);
      if (i == 0) {
          i = 1;
          goto begin;
      }
  }

  {
      int i = 1;
      begin:
      printf("i == %d\n", i);
      if (i == 1) {
          i = 0;
          goto begin;
      }
  }

}

Le code ci-dessus ne se compile pas et produit l'erreur suivante

/tmp/gH2TVYnTSJ.c: In function 'main':
/tmp/gH2TVYnTSJ.c:17:5: error: duplicate label 'begin'
   17 |     begin:
      |     ^~~~~
/tmp/gH2TVYnTSJ.c:7:5: note: previous definition of 'begin' was here
    7 |     begin:
      |     ^~~~~

Bien que i puisse être définie dans chaque bloc, les noms d'label doivent être uniques dans une fonction. C'est parce que les labels ont une portée de fonction, ce qui signifie que chaque label est vu par la fonction, quel que soit l'endroit de la fonction où il est défini. Ainsi, contrairement aux variables, quel que soit l'endroit où un label est défini dans une fonction, il doit porter un nom unique au sein de cette même fonction. Pour que le code fonctionne, il faut changer le nom de l'un des labels afin que les deux soient uniques, comme dans l'exemple suivant

#include <stdio.h>

int main() {

    {
        int i = 0;
        begin:
        // prints
        // i == 0
        // i == 1
        printf("i == %d\n", i);
        if (i == 0) {
            i = 1;
            goto begin;
        }
    }

    {
        int i = 1;
        start:
        // prints
        // i == 1
        // i == 0    
        printf("i == %d\n", i);
        if (i == 1) {
            i = 0;
            goto start;
        }
    }

}

Les instructions sur une seule ligne peuvent être « empilées » tout en restant dans un même bloc, sans utiliser d'accolades. Dans l'exemple ci-dessous, l'instruction for contient une instruction if, qui contient elle-même une instruction printf. Bien qu'il y ait en tout deux instructions dans le bloc for, seule son instruction if sur une seule ligne est évaluée comme corps de la boucle for. L'instruction printf sur une seule ligne de l'instruction if est à son tour évaluée comme son corps. Si des blocs imbriqués ne font chacun qu'une seule ligne, aucun d'eux n'a besoin d'accolades. À noter que le commentaire n'exige pas d'accolades pour l'englober, lui et l'instruction printf du bloc if, puisque le commentaire n'est pas une instruction. L'indentation montre clairement ce qui se passe, mais une équipe de développement peut avoir ses propres règles de codage sur le placement des accolades dans ce genre de situation, afin de lever toute ambiguïté.

#include <stdio.h>

int main() {

    for (int i = 0, j = 11; i < j; i++)
        if (i & 1 == 1)
            // prints 1 3 5 7 9 
            printf("%d ", i);
}

On a vu que les valeurs définies dans un bloc ne sont pas accessibles depuis l'extérieur de ce bloc. Et on a vu que les valeurs définies à l'extérieur d'un bloc sont généralement accessibles depuis l'intérieur du bloc. Il est possible qu'une variable extérieure à un bloc soit masquée à l'intérieur de ce bloc si l'identifiant de cette variable est utilisé pour définir une variable du bloc. Dans l'exemple suivant, l'identifiant number est utilisé à la fois pour une variable au niveau de la fonction et pour une variable au niveau du bloc. On dit que la variable au niveau du bloc masque, ou occulte, la variable au niveau de la fonction.

#include <stdio.h>

int main() {
    int number = 1;
    // prints number in function is 1
    printf("number in function is %d\n", number);
    
    if (number == 1) {
        // the definition of a block-level variable using the same name
        // shadows the function-level variable
        int number = 2;
        // prints number in function is 2
        printf("number in if block is %d\n", number);
    }
    // prints number in function is 1
    printf("number in function is %d\n", number);
}

Les compilateurs peuvent générer une erreur si l'on tente de masquer un paramètre de fonction. L'exemple suivant

#include <stdio.h>

void print_number(int number) {
    // will fail to compile
    int number = 2;
    printf("number in function is %d\n", number);
}

int main() {
    int number = 1;
    printf("number in main is %d\n", number);
    print_number(number);
    printf("number in main is %d\n", number);
}

produit les erreurs suivantes sur deux plateformes

/tmp/jIvhD5UOop.c: In function 'print_number':
/tmp/jIvhD5UOop.c:5:9: error: 'number' redeclared as different kind of symbol
    5 |     int number = 2;
      |         ^~~~~~
/tmp/jIvhD5UOop.c:3:23: note: previous definition of 'number' was here
    3 | void print_number(int number) {
      |                   ~~~~^~~~~~

et

redefinition of formal parameter 'number'
Modifie via GitHub Le lien s'ouvre dans une nouvelle fenêtre ou un nouvel onglet