Glossaire langage C



Affectation


  • En C, l'opérateur d'affectation de base est noté =
    Cet opérateur binaire est utilisable dans toute expression dont l'opérande gauche possède une adresse (on parle de Lvalue [Left value]). Cela exclut donc toutes les constantes (constantes numériques, résultats d'expressions arithmétiques, pointeurs constants tels que noms de tableaux et noms de fonctions)
  • On parle d'opérateur à effet de bord car il modifie la valeur de l'emplacement en mémoire associé.
    Une expression telle que x = 5 dans laquelle la variable x est de type int a comme valeur 5 et affecte à la variable x la valeur 5.
  • Il existe par ailleurs des opérateurs d'affectation étendue : +=, -=, *=, /=, %=, ^=, |=, &=, |=, &=, <<= et >>=.
    De manière générale, une expression «  a a= b  » est équivalente à «  a = a a (b)  »


Blocs (ou instructions composées)


  • Il s'agit là de regrouper une séquence d'instructions pour en permettre l'utilisation comme s'il s'agissait d'une instruction élémentaire.
  • Pour constituer un bloc, il suffit de faire précéder la première instruction qui lui appartient d'une accolade ouvrante { et de faire suivre la dernière (c'est-à-dire le ; qui en marque la fin) d'une accolade fermante }
  • Des déclarations ou définitions de variables peuvent par ailleurs être ajoutées n'importe où dans le bloc (mais la lisibilité du programme plaide pour que ces déclarations et déclarations soient placées en début de bloc, certains compilateurs l'imposant) : la portée de ces identificateurs (la partie du code où elles seront visibles) sera limitée à la suite des instructions qui les suivent et qui appartiennent au bloc.
  • Les blocs peuvent être emboîtés les uns dans les autres : autrement dit on peut définir un bloc à l'intérieur d'un autre (le bloc extérieur).
    La déclaration d'une variable faite dans un bloc est visible dans tous les blocs définis à l'intérieur de ce bloc, à moins qu'une nouvelle définition de cette variable ne soit faite à l'intérieur de ce bloc : la règle est qu'une nouvelle définition masque la définition extérieure.
  • L'exemple suivant illustre avec des couleurs l'emboîtement de blocs et la visibilité des identificateurs homonymes :

    --> cat bloc.c
    #include <stdio.h>
    #include <stdlib.h>
    int main( )
    { // début du bloc 1
       int a = 1;
       { // début du bloc 1.1
          printf("%d\n", a);
          int a = 10;
          printf("%d\n", a);
          { // début du bloc 1.1.1
             printf("%d\n", a);
             int a = 100;
             printf("%d\n", a);
             a ++;
             printf("%d\n", a);
          } // fin du bloc 1.1.1
          a ++;
          printf("%d\n", a);
       } // fin du bloc 1.1
       { // début du bloc 1.2
          printf("%d\n", a);
          int a = 1000;
          printf("%d\n", a);
          a ++;
          printf("%d\n", a);
       } // fin du bloc 1.2
       printf("%d\n", a);
       return EXIT_SUCCESS; // return 0
    } // fin du bloc 1

L'exécution de ce programme fournit les résultats suivants :


    --> ./bloc
    1
    10
    10
    100
    101
    11
    1
    1000
    1001
    1


break


  • L'instruction « break; » est utilisable à l'intérieur d'un switch ou d'une boucle (de l'une quelconque des trois formes).
  • Elle provoque la sortie de cette instruction, c'est-à-dire la poursuite de l'exécution à l'instruction suivante.


Comparaison (opérateurs relationnels)


Ces opérateurs permettent de comparer des valeurs numériques ou des pointeurs :

  • == : égalité
  • != : inégalité
  • < : strictement plus petit
  • > : strictement plus grand
  • <= : plus petit ou égal
  • >= : plus grand ou égal


const


  • A côté de la possibilité de définir des constantes (au niveau du préprocesseur avec la directive #define ou au niveau du compilateur, pour des constantes entières, avec le concept d'énumération), il est dorénavant (avec la norme ANSI) possible de définir des variables non modifiables.
  • Ces variables doivent être :
  • initialisées lors de leur définition;
  • qualifiées constantes avec le mot clé const.


  • Le mot clé const peut être placé avant ou après le nom du type comme dans

    const int a = 1;
    int const b = 2;

De telles définitions de m et n entrainent une erreur lors de la compilation pour des tentatives de modification des valeurs de ces variables :

    a ++; // error: increment of read-only variable 'a'
    b = 2; // error: assignment of read-only variable 'b'


Si les deux écritures sont équivalentes dans le cas de variables de type classique (types de base, structures ou unions), les choses sont un peu différentes dans le cas de la définition de variables de type pointeur :

  • s'il s'agit de définir un pointeur constant, on écrira :

int a, b;
int * const ptr = &a;


Le fait que ptr soit constant, interdit dans la suite du programme toute instruction modifiant le pointeur :


ptr = &b; // erreur de compilation: assignment of read-only variable 'ptr'

  • s'il s'agit de définir un pointeur sur quelque chose de constant, on écrira :

    int a, b;
    int const * ptr = &a;

Le fait que l'espace pointé par ptr soit déclaré constant, interdit dans la suite du programme l'instruction

*ptr = 12; // erreur de compilation: assignment of read-only location


Il est intéressant d'observer ce qui se passe dans la séquence suivante : un simple avertissement (warning) signale que le pointeur référence une variable qualifiée const, ce que la définition du pointeur ne spécifie pas :
int const a;
int *ptr=&a; // warning:initialization discards qualifiers from pointer target type


Un exécutable est donc produit. L'exécution de l'instruction suivante, qui modifie indirectement la variable supposée constante n'a donné lieu à aucune erreur (mais un tel comportement non souhaitable n'est pas garanti sur tout système et risque de conduire sur certains à un arrêt brutal de l'exécution pour violation mémoire) :

*ptr = 1000;
printf("%d\n", a);


  • s'il s'agit de définir un pointeur constant sur quelque chose de constant, on pourra par exemple écrire :

    const int a;
    int const * const ptr = &a;


continue


  • L'instruction « continue; » est utilisable à l'intérieur d'une boucle (de l'une quelconque des trois formes).
  • Elle provoque le passage à l'itération suivante de cette boucle.

Cela signifie que:

  • pour une boucle while ou do, le passage au test contrôlant la boucle, puis le cas échéant (si le résultat obtenu a comme valeur logique Vrai) nouvelle exécution du corps de la boucle;
  • pour une boucle for(e1; e2; e3) l'évaluation immédiate de l'expression e3, puis celle de l'expression e2. Si la valeur obtenue a comme valeur logique Vrai le corps de la boucle est exécuté de nouveau et sinon on sort de la boucle.


Décrémentations (autodécrémentations)


L'opérateur -- est un opérateur unaire (un argument) applicable à un argument adressable (Lvalue).
C'est un opérateur à effet de bord (modifiant la valeur de l'espace mémoire correspondant à son argument) qui est utilisable sous deux formes :

  • en forme préfixée (pré-décrémentation) : la valeur de la variable x étant 4, l'expression --x
  • décrémente la valeur de la variable x dont la nouvelle valeur est donc 3 (effet de bord)
  • a comme valeur la nouvelle valeur de x, c'est-à dire 3
  • en forme postfixée (post-décrémentation) : la valeur de la variable x étant 4, l'expression x--
  • décrémente la valeur de la variable x dont la nouvelle valeur est donc 3 (effet de bord)
  • a comme valeur l'ancienne valeur de x, c'est-à 4

do ... while


  • La forme générale de la boucle est :

do
  {
     suite_instructions
    }
while (expression)

  • Cette structure de contrôle est assez semblable à la structure while dans le sens où elle permet d'itérer l'exécution d'une suite d'instructions tant qu'une certaine condition est vraie.
  • La différence est dans le fait que le test contrôlant l'itération est fait en fin d'itération et non en début comme c'est le cas pour les boucles while.
    Une conséquence en est que le corps d'une boucle do ... while est exécutée au moins une fois.


Effet de bord


On parle d'effet de bord dans le cas d'un opérateur (par exemple =, ++ ou -=) modifiant l'un de ses opérandes ou d'une fonction qui modifie la valeur d'une variable globale ou d'une zone pointée par un de ses paramètres.


Expressions composées


L'opérateur « , » permet de composer deux expressions.

Ainsi, une expression de la forme

         exp1,exp2

est une expression dont l'évaluation entraîne successivement :

  • l'évaluation de exp1;
  • l'évaluation de exp2;
  • c'est cette dernière évaluation qui donne le type et la valeur à l'expression composée.

Ainsi l'évaluation de l'expression

         x = (y = 4, z = 2 * y)

entraîne l'évaluation successive des expressions

  • « y = 4 » de valeur 4 (avec effet de bord affectant 4 à la variable y),
  • «, z = 2 * y » de valeur 8 (avec effet de bord affectant 8 à z).
    Cette valeur est celle de l'expression composée et elle est finalement affectée à la variable x.


Expressions conditionnelles


  • Etant données trois expressions exp1, exp2 et exp3, on en déduit une expression conditionnelle par la forme

         exp1?exp2:exp3

dont l'évaluation est la suivante :

  • évaluation de l'expression exp1
  • si la valeur logique de cette évaluation est vraie, évaluation de l'expression exp2, et la valeur obtenue est la valeur de l'expression conditionnelle;
  • sinon, évaluation de l'expression exp3, et la valeur obtenue est la valeur de l'expression conditionnelle.


  • Quelques exemples d'expressions conditionnelles :
  • l'expression « x >= 0 ? x : -x » calcule la « valeur absolue de x »;
  • si les valeurs de a et b sont respectivement 13 et 11, l'expression

        (a > 5) ? ((b <= 150) ? 3.25 : 9) : -13.0

a pour valeur 3.25.


Fonctions


Définition de fonction


La définition d'une fonction est constituée de

  • l'en-tête de la fonction constituée successivement de :
  • éventuellement le qualificatif static.
    S'il est présent, la définition de la fonction ne sera visible que dans le fichier où elle est définie.
  • le type de la valeur retournée par la fonction
  • le nom de la fonction
  • une parenthèse ouvrante (
  • une liste de déclarations de paramètres (chacune conteint un type et un nom de paramètre formel) séparées par une virgule
  • une parenthèse fermante )
  • le bloc de définition de la fonction, constitué comme tout bloc, dans l'ordre de :
  • une accolade ouvrante {
  • des définitions de variables qui seront locales au bloc
  • une suite d'instructions et de blocs
  • une accolade fermante }

Une seule définition peut être attachée à un nom de fonction. Il n'y a pas la possibilité, comme en C++ ou Java, de donner plusieurs définitions de fonctions sous le même nom mais différant par leur signature (nombre ou type des paramètres).


Appel de fonction


L'évaluation et la transmission des paramètres depuis le module appelant vers le module appelé possèdent les propriét&eacutes importantes suivantes :

  • la norme ne dit rien sur l'ordre dans lequel l'évaluation des paramètres est réalisé : il dépend des implémentations.

Cela signifie qu'il faut bannir les effets de bord dans les expressions utilisées dans les paramètres d'appel des fonctions.
 

  • le passage des paramètres est réalisé par valeur.

Cela signifie que pour chacun des paramètres, une variable locale (portant le nom du paramètre formel) est créée sur la pile. Cette variable est initialisée avec la valeur du paramètre d'appel correspondant.


Prototype d'une fonction (déclaration d'une fonction)


  • Le prototype d'une fonction en donne une description en vue de son utilisation sans qu'on en connaisse la définition complète. Celle-ci peut être donnée plus loin dans le code ou faire partie d'un code dans un autre fichier, voire être disponible sous forme binaire dans une bibliothèque (dans ce dernier cas, le prototype est normalement donné dans un fichier d'interface de la bibliothèque [un fichier d'extension .h] et le source d'un programme utilisant la bibliothèque est supposé inclure ce fichier [#include]).

Le prototype d'une fonction doit normalement être décrit avant sa première utilisation (voir cependant la remarque).

Un tel prototype permet au compilateur de « vérifier » que l'utilisation de la fonction est conforme à sa définition.

  • A ces fins, un prototype est constitué de :
  • de manière optionnelle, le mot-réservé extern ;
  • le type de valeur retournée par la fonction ;
  • le nom de la fonction ;
  • les types des paramètres de la fonction.

Il est suivi d'un point virgule et non du corps de la fonction (contenant les instructions à exécuter), et ne nomme pas a priori les paramètres (s'il en contient nous parlerons de prototype étendu).

  • Remarques :
  • la définition d'une fonction en constitue de facto un prototype dans ce qui la suit dans le fichier où elle est définie.
  • il existe un prototype par défaut pour les fonctions. Ce type est automatiquement attribué par le compilateur C à une fonction lors du premier appel si un prototype ne lui pas été associé préalablement.
  • le type par défaut de la valeur retournée par une fonction est int
  • la liste des paramètres par défaut est vide (attention cela n'est pas équivalent au type void : une telle fonction ne peut avoir de paramètre lors d'un appel). Avec ce prototypage par défaut (qui n'en est pas vraiment un), tout appel de la fonction sera alors correct, avec n'importe quel nombre de paramètres et n'importe quel type de ces paramètres.

Ce petit exemple illustre ce que nous venons de dire :


    --> cat prototype.c

    #include <stdio.h>

    #include <stdlib.h>

    void g(void){

       printf("Bonjour\n"); 

    }

    int main( ) {

       float x = f(3, 5);

       g(3);

       printf("%f\n", x);

       return EXIT_SUCCESS;

    }

    int f(int x, int y, int z){

       return x + y + z;

    }

    --> gcc prototype.c

    prototype.c: In function 'main':

    prototype.c:6: warning: implicit declaration of function 'f'

    prototype.c:7: error: too many arguments to function 'g'

    -->

  • Des exemples de prototypes :

    /* deux paramètres de type int et retour d'un int */
    int somme(int, int);
    /* un paramètre de type size_t et retour d'un pointeur sur char */
    void *malloc (size_t);
   


for


  • Il s'agit de la forme la plus générale de boucle dont la syntaxe est la suivante :

for(exp_initialisation; exp_condition; exp_modification)
    instruction

dans laquelle :

  • « exp_initialisation; » est une instruction simple réalisant typiquement une série d'initialisations comme par exemple 
  • i = 1;
  • i = 0, j = 2, k = n;
  • « exp_condition » est une expressison dont la valeur logique servira à contrôler la boucle comme par exemple :
  • i < n;
  • i > j + k
  • i < m && j + k <= n && t[i][j+k] != x;
  • « exp_modification; » est une instruction simple réalisant typiquement une série de modifications de variables entrant utilisées dans l'expression utilisée pour contrôler la boucle.
    Des exemples de telles instructions sont :
  • i++;
  • i++, j += 2, k -= 3;


 Une telle boucle est équivalente à la boucle while suivante :


exp_initialisation;

while(exp_condition)

   {

        instruction

        exp_modification;

   }


Identificateur


  • Il s'agit d'une suite de caractères commençant par une lettre (minuscule ou majuscule, caractère « _ »), les caractères suivants étant des lettres ou des chiffres.
  • La longueur d'un identificateur n'est pas limitée.


if


Il s'agit d'une structure de contrôle permettant de réaliser des tests et d'exécuter des instructions ou blocs différents selon la valeur de ces tests.
Dans ce qui suit instruction désigne soit une instruction simple, soit un bloc (délimité par des accolades) et expression désigne une expression quelconque dont on considérera la valeur logique déduite de sa valeur..

  • forme simple :

if (expression)
    instruction

L'instruction n'est exécutée que si la valeur logique de l'expression est vraie

  • forme générale :

if (expression)
    instruction1
else
    instruction2

L'expression est évaluée : si sa valeur logique est vraie, l'instruction1 est exécutée et sinon c'est l'instruction2 qui est exécutée.

  • imbrication :
    plusieurs if peuvent être imbriqués.
    Dans ce cas, chaque else est associé au if le plus proche pas encore associé à un autre else.

Dans la séquence suivante, les if et else de même couleur sont associés :

if(a)
     if(b)
         x = 3;
     else if(c)
            x = 4;
          else {
              if(d)
                  x = 5;
               }
else
     x = 6;


Incrémentations (autoincrémentations)


L'opérateur ++ est un opérateur unaire (un argument) applicable à un argument adressable (Lvalue).
C'est un opérateur à effet de bord (modifiant la valeur de l'espace mémoire correspondant à son argument) qui est utilisable sous deux formes :

  • en forme préfixée (pré-incrémentation) : la valeur de la variable x étant 4, l'expression ++x
  • incrémente la valeur de la variable x dont la nouvelle valeur est donc 5 (effet de bord)
  • a comme valeur la nouvelle valeur de x, c'est-à dire 5
  • en forme postfixée (post-incrémentation) : la valeur de la variable x étant 4, l'expression x++
  • incrémente la valeur de la variable x dont la nouvelle valeur est donc 5 (effet de bord)
  • a comme valeur l'ancienne valeur de x, c'est-à 4


Instructions élémentaires (ou simples)


Toute instruction élémentaire se déduit d'une expression en lui ajoutant le caractère « ; »


Le caractère « ; » fait donc partie de l'instruction et en marque la terminaison.
Ce n'est pas, comme c'est le cas dans d'autres langages (Pascal par exemple), un opérateur de composition ou un séparateur d'instructions.

Remarque : une instruction telle que « i + 1; » est correcte mais ne fait rien puisqu'elle ne modifie rien en mémoire (pas d'effet de bord).


Instruction vide


L'instruction « ; » réduite à un point virgule est l'instruction vide. Elle est utilisable par exemple comme corps de boucle ou dans une instruction conditionnelle en partie if ou else.


main


La fonction main constitue le « point d'entrée » dans un binaire exécutable, c'est-à-dire qu'au lancement du programme, c'est cette fonction qui est automatiquement appelée.

Ainsi :

  • un binaire exécutable (génériquement appelé a.out) ne peut être construit (de manière ultime par l'éditeur de liens) que si une fonction main est définie dans l'application;
  • au lancement de l'application, la fonction main reçoît des informations lors de ce lancement sous forme de paramètres;
  • la fonction main peut « faire remonter » une valeur entière lors de sa terminaison en particulier au travers d'un appel de return ou via un appel à la primitive exit.
    C'est pourquoi la valeur du type retourné attendu est int.
    Au niveau du système, cette valeur définit le code de retour du processus à sa terminaison.

Arguments/paramètres de la fonction main : la fonction est normalement appelée sous deux formes (c'est ce qu'attend par exemple le compilatuer gcc) :

  • sans paramètre, c'est-à-dire sous la forme « main( ) »;
  •  
  • avec deux paramètres, c'est-à-dire sous la forme « main(argc, argv) » où :
  • argc est de type int;
  • argv est un tableau de pointeurs sur caractère, c'est-à-dire déclaré sous la forme « char *argv[] ».

L'idée est que, lorsque l'application est lancée, elle reçoît de l'appelant (en l'occurrence le système d'exploitation) des informations.

Consultation d'un programme parcourant les paramètres.


Opérateurs logiques (booléens)


Ils permettent de composer de composer les valeurs logiques d'expression et par là de réaliser des tests généraux.

Quatre opérateurs sont disponibles :

  • un opérateur unaire, la négation qui est notée !
  • trois opérateurs binaires :
  • la conjonction (ET) notée &&
  • la disjonction (OU) notée ||
  • le XOR (OU exclusif) noté ^


Priorité des opérateurs


Il existe 15 niveaux de priorité.

Pour chaque niveau, l'associativité est réalisée de gauche à droite ou de droite à gauche comme indiqué.
A titre d'exemple, l'associativité des opérateurs arithmétiques additifs est réalisée de gauche à droite et ces opérateurs sont moins prioritaires que les opérateurs arithmétiques mutiplicatifs.
Cela signifie que l'expression
        3-4+5*6-7-8+1
est évaluée comme
       (((((3-4)+(5*6))-7)-8)+1)
et a donc 16 comme valeur.

L'associativité des opérateurs d'affectation étant de droite à gauche et l'addition étant prioritaire par rapport aux affectations, l'expression
        a=b+=b*=c=2+b
est évaluée comme
        a=(b+=(b*=(c=(2+b))))
Si b vaut initialement 3, après évaluation de cette expression, les nouvelles valeurs des variables a, b et c sont respectivement (par effet de bord) 30, 30 et 5.


Premier niveau : indexation, fonction, structure


Opérateur

Notation

Exemples

Associativité

indexation

[ ]

t[i][j]

de gauche à droite

accès aux champs

.  ->  

c.champ    pointeur->champ

de gauche à droite

appel de fonction

( )

f(a, b, 3)

de gauche à droite


Deuxième niveau : opérateurs unaires


Opérateur

Notation

Exemples

Associativité

négation logique

!

!(x == 4)    !x

de droite à gauche

négation bit à bit

˜

˜ent    !x

de droite à gauche

incrémentation

++

++x    x++

de droite à gauche

décrémentation

--

--x    x--

de droite à gauche

moins unaire

-

-(x + 1)

de droite à gauche

coercition (cast)

(type)

(char *)    (float)

de droite à gauche

taille

sizeof

sizeof(int)    sizeof(n)

de droite à gauche

référencement (adresse de)

&

&n

de droite à gauche

déréférencement (valeur pointée)

*

*ptr

de droite à gauche


Troisième niveau : opérateurs arithmétiques multiplicatifs


Opérateur

Notation

Exemples

Associativité

mutiplication

*

5 * x

de gauche à droite

division

/

x / y

de gauche à droite

modulo

%

x % 5

de gauche à droite


Quatrème niveau : opérateurs arithmétiques additifs


Opérateur

Notation

Exemples

Associativité

addition

+

x + 8

de gauche à droite

soustraction

-

a - b

de gauche à droite


Cinquième niveau : opérateurs de décalage


Opérateur

Notation

Exemples

Associativité

décalage à gauche

<<

a << 4

de gauche à droite

décalage à droite

>>

x >> n

de gauche à droite


Sixième niveau : opérateurs relationnels (plus grand et plus petit)


Opérateur

Notation

Exemples

Associativité

plus grand

>

a > b

de gauche à droite

plus grand ou égal

>=

a >= 5

de gauche à droite

plus petit

<

y < a

de gauche à droite

plus petit ou égal

<=

a <= b

de gauche à droite


Septième niveau : opérateurs relationnels (égal, différent)


Opérateur

Notation

Exemples

Associativité

égalité

==

(x + 1) == (y + 2)

de gauche à droite

négalité

!=

z != 5

de gauche à droite


Huitième niveau : ET bit à bit


Opérateur

Notation

Exemples

Associativité

ET bit à bit

&

x & 0x3456AF01

de gauche à droite


Neuvième niveau : XOR bit à bit (OU exclusif)


Opérateur

Notation

Exemples

Associativité

XOR bit à bit

^

x ^ y

de gauche à droite


Dixième niveau : OU bit à bit (OU exclusif)


Opérateur

Notation

Exemples

Associativité

OU bit à bit

|

x | 0234

de gauche à droite


Onzième niveau : ET logique


Opérateur

Notation

Exemples

Associativité

conjonction logique (ET)

&&

a > b) && (z < 5)

de gauche à droite


Douzième niveau : OU logique


Opérateur

Notation

Exemples

Associativité

disjonction logique (OU)

||

(a != 0) || (b / a > 5)

de gauche à droite


Treizième niveau : expression conditionnelle


Opérateur

Notation

Exemples

Associativité

expression conditionnelle

?  :

(x>0)?x:-x

de droite à gauche


Quatorzième niveau : affectations


Opérateur

Notation

Exemples

Associativité

affectation simple

=

a = 5

de droite à gauche

affectations étendues (arithmétiques)

+=  -=  *=  /=  %=

a+=5  a*=(b+1)

de droite à gauche

affectations étendues (opérations bit à bit)

&=  ^=  |=

a&=b  a^=0x789A

de droite à gauche

affectations étendues (décalages)

<<=  >>=

a<<=5

de droite à gauche


Quinzième niveau : composition d'instructions


Opérateur

Notation

Exemples

Associativité

composition d'instructions

,

a=2,b=a+3,z+=5

de gauche à droite


switch


Souvent appelée aiguillage, cette structure permet, après évaluation d'une expression, de « se brancher » (et donc d'y poursuivre l'exécution) en un point (une étiquette) correspondant à la valeur de l'expression.

La syntaxe de la structure switch est la suivante (chaque suite_instructions peut éventuellement être vide et consiste sinon en la concaténation d'instructions, dont l'instruction « break; ») :

    switch ( expression_entière )

       {

         case constante1 : suite_instructions1

         case constante2 : suite_instructions2

              ........

         case constanteN : suite_instructionsN

         default : suite_instructions

       }

    // point de reprise d'exécution en cas de break dans le switch

La dernière alternative, notée default correspond au cas par défaut (si la valeur de l'expression ne correspond à aucun des cas prévus).


Elle est facultative. Cependant, pour des raisons de lisibilité des programmes, son usage est conseillé, quitte à lui associer une liste d'instructions vide;

Les étiquettes de chacun des cas sont des expressions constantes (que le compilateur doit donc pouvoir calculer).

la sémantique de cette structure de contrôle est la suivante :

  • L'expression (à valeur entière) contrôlant l'instruction est évaluée;
  • l'exécution reprend sur la liste d'instructions repérée par l'« étiquette  » associée à la valeur de l'expression.
  • l'exécution se poursuit en séquence. Elle ne se limite pas à la liste des instructions allant jusqu'à l'alternative suivante.
    Si on souhaite interrompre l'enchaînement des instructions, il faut utiliser l'instruction « break; » qui provoque un saut à l'instruction suivant l'instruction switch.

un exemple utilisant un switch pour imprimer un message différent selon la nature du caractère contrôlant l'instruction :

    switch(c) {

        case 'A': case 'E': case 'I': case 'O': case 'U': case 'Y':

           printf("Le caractere lu est une voyelle\n");

           break;

        case '0': case '1': case '2': case '3': case '4':

        case '5': case '6': case '7': case '8': case '9':

           printf("Le caractere lu est un chiffre\n");

           break;

        case 'B': case 'C': case 'D': case 'F': case 'G':

        case 'H': case 'J': case 'K': case 'L': case 'M':

        case 'N': case 'P': case 'Q': case 'R': case 'S':

        case 'T': case 'V': case 'W': case 'X': case 'Z':

           printf("Le caractere lu est une consonne\n");

           break;

        default :

           printf("Le caractere lu n'est ni une lettre, ni un chiffre\n");

           // break;

    }


Le type void


Il a été introduit avec la norme ANSI du langage pour :

  • permettre de définir des fonctions ne renvoyant pas de valeurs (telles que les procédures ou sous-programmes proposés dans d'autres langages);
  • permettre de spécifier qu'une fonction ne possède pas de paramètres;
  • permettre de spécifier l'utilisation de pointeurs génériques compatibles avec les pointeurs de tout type.


Le type void est un type incomplet (de taille non définie) : il correspondant à une absence de type et par suite :


  • il est interdit de définir ou déclarer des variables de type void

    --> cat essai.c

    int main(){void a; }

    --> gcc a.c

    essai.c: Dans la fonction « main »:

    essai.c:2: error: variable or field `a' declared void

    -->


  • par contre il est possible (et c'est en grande partie pour cela que ce type a été introduit) de définir des pointeurs sur void.


  • Il convient d'interpéter ce type comme le type « pointeur sur un type quelconque (ou non connu) ».


  • Ce type de pointeur joue d'une certaine manière le rôle de pointeur générique. Une fonction telle que malloc alloue dynamiquement de la mémoire sans préjuger de l'usage qui en sera fait et de la structure précise de l'espace alloué. 
  • Elle renvoie simplement l'adresse d'une zone d'une certaine taille, la définition de la structure de cet espace est laissée à l'application : il n'est même plus nécessaire de faire un cast explicite (le type « pointeur sur void » qui correspond à « void * » est compatible avec tous les types pointeurs).


  • Le type de ce qui est pointé n'étant pas connu, il n'est pas possible d'en connaitre la taille et par suite il n'est pas possible de faire avec les pointeurs sur void le même type d'arithmétique qu'avec les autres pointeurs, ni de les déréférencer (c'est-à-dire accéder à la zone pointée avec l'opérateur *).


  • Remarque : certaines implémentations du langage C, comme gcc, assimilent un pointeur sur void à un pointeur sur char. Cela autorise l'arithmétique sur ces pointeurs. Mais l'utilisation de cette possibilité limite la portabilité des applications.


while


La forme générale de la boucle est :

while (expression)
   instruction

instruction est une instruction simple ou un bloc.

Il s'agit d'une structure de contrôle permettant d'itérer une instruction (élémentaire ou bloc) tant qu'une certaine condition est vraie.

L'expression contrôlant la boucle est évaluée à l'entrée de la boucle et donc l'instruction n'est éventuellement pas exécutée.


Créé avec HelpNDoc Personal Edition: Créer des documentations web iPhone