Diaporama n°7¶

Le langage C - Introduction et bases du langage¶

I. Un premier exemple¶

II. Les principales caractéristiques du C¶

III. Le langage C, en bref¶

I. Un premier exemple¶

In [24]:
#include <stdio.h>

/* Mon premier programme en C, 
   un classique de l'informatique ! */

int main() {
    printf("Hello world !\n"); // affichage
    return 0;
}
Hello world !

Quelques remarques sur la syntaxe¶

  • #include <stdio.h> : directive du préprocesseur (???) qui permet d' inclure le fichier d'en-tête stdio.h (???)

    • permet d' utiliser des fonctions définies par ailleurs (un peu comme import en Python)
  • /* ... */ : désigne un commentaire, éventuellement sur plusieurs lignes
  • // : commentaire jusqu'à la fin de la ligne
  • int : type entier
  • int main() { ... } : définition de fonction
    • le corps de la fonction est écrit entre accolades
    • la fonction main ne prend aucun paramètre et la valeur de retour est de type entier

NB : pas de def, pas de :

  • printf(...) : appel de fonction
    • printf permet l'affichage sur la sortie standard (cf cours Linux)
    • la fonction printf utilisable ici car elle est déclarée (??) dans le fichier stdio.h
  • ; : à mettre après chaque une instruction
  • "Hello world !\n" : chaîne de caractères.
    • \n : retour à la ligne, caractère spécial qui compte pour un seul caractère
  • return 0; : valeur de retour de la fonction

image.png

Indentation ?¶

En C, l'indentation n'est pas indispensable, ni signifiante.

On utilise l'indentation pour améliorer la lisibilté du code.

In [4]:
#include <stdio.h>
int main() { printf("Hello world !"); return 0; }
Hello world !

Les puristes du C écrivent plutôt de la façon suivante (mais le code occupe davantage de lignes) :

In [26]:
#include <stdio.h>

int main() 
{ 
    printf("Hello world !"); 
    return 0; 
}
Hello world !

image.png

Quelques remarques sur l'exécution du code¶

En C, la fonction main (fonction principale) est la fonction appelée au début de l'exécution du programme.

La fonction main doit obligatoirement être présente dans le code à exécuter.

In [23]:
#include <stdio.h>
int toto() { 
    printf("Hello world !"); 
    return 0; 
}
/tmp/tmplrvt60rz.c: In function ‘main’:
/tmp/tmplrvt60rz.c:5:1: warning: ISO C forbids nested functions [-Wpedantic]
    5 | int toto() {
      | ^~~
At top level:
/tmp/tmplrvt60rz.c:5:5: warning: ‘toto’ defined but not used [-Wunused-function]
    5 | int toto() {
      |     ^~~~

Quelques messages d'erreur¶

Comme en Python, certaines erreurs sont détectées avant l'exécution, d'autres pendant l'exécution

In [4]:
#include <stdio.h>
int main() { 
    printf("Hello world !\n")
    return 0;
}
/tmp/tmpeolijl2q.c: In function ‘main’:
/tmp/tmpeolijl2q.c:3:30: error: expected ‘;’ before ‘return’
    3 |     printf("Hello world !\n")
      |                              ^
      |                              ;
    4 |     return 0;
      |     ~~~~~~                    
[C kernel] GCC exited with code 1, the executable will not be executed
In [3]:
#include <stdio.h>
int main() { 
    printf("Hello world !\n"); 
    return 0/0; 
}
/tmp/tmp1mpn9jf5.c: In function ‘main’:
/tmp/tmp1mpn9jf5.c:4:13: warning: division by zero [-Wdiv-by-zero]
    4 |     return 0/0;
      |             ^
Hello world !
[C kernel] Executable exited with code -8

II. Les principales caractéristiques du C¶

Historique¶

Le langage C a été conçu au début des années 1970, conjointement à la réalisation du système d'exploitation (OS) Unix (cf Diapo Systèmes d'exploitation).

  • Ken Thompson (concepteur d'Unix)

  • Dennis Ritchie (concepteur du langage C, co-concepteur d'Unix)

  • Brian Kernighan (premier auteur d'un livre sur C avec D.Ritchie : le K&R)

Historique¶

A l'époque, on programmait directement en langage machine. Mais les ordinateurs devenaient de plus en plus complexes ...

Principe d'un système d'exploitation (OS) : pouvoir s'abstraire du fonctionnement de la machine

Comment coder l'OS ?

  • Pas en langage machine ...

  • Début des langages de programmation ... (Algol, Fortran)

  • invention d'un nouveau langage : le C ! (à partir d'un langage appelé B)

Historique¶

C était considéré comme un langage de "haut niveau", c'est-à-dire loin du fonctionnement de la machine.

Largement adopté (un peu comme Python aujourd'hui), le langage C est normalisé dans les années 1980.

NB : la normalisation permet d'éviter les incompatibilités entre différentes implémentations

L'inertie des normes¶

Du fait de la normalisation, le langage C n'a que peu évolué depuis et présente plusieurs défauts importants qui persisteront probablement.

  • pas de mécanisme d'exceptions (cf OCaml, en MPI)
  • pas de structure de données génériques
  • pas de système de nommage
  • pas de gestion automatique de la mémoire

Historique¶

Aujourd'hui C est considéré comme un langage de "bas niveau", mais reste très utilisé, pour des raisons de performances.

Le langage C a grandement influencé les langages plus récents (C++, C#, Java, Python ...) dont les implémentations sont souvent, pour partie, écrites en C.

Exécution : compilé vs interprété¶

En Python, le code est lu puis exécuté par un programme (l'interpréteur Python : python).

On dit que le langage Python est un langage interprété.

Exécution : compilé vs interprété¶

En C, le code n'est pas exécuté tout de suite...

  • Il est d'abord traduit en fichier exécutable (code machine) par un programme qui est appelé compilateur.

  • Le programme est ensuite exécuté directement par la machine (sans passer par un programme intermédiaire).

On dit que le langage C est un langage compilé.

Exécution : compilé vs interprété¶

  • langage interprété : la machine exécute un programme (l'interpréteur ou l'interprète) qui exécute mon programme

  • langage compilé : une fois compilé (par le compilateur, la machine exécute directement mon programme

NB : quelques compilateurs pour le langage C : gcc, cc, clang

NB : quelques interpréteurs pour le langage Python : python3 (cpython), jython, ...

Exécution : compilé vs interprété¶

Comparaison : Python vs. C¶

Code Python :¶

image.png

La commande pour exécuter ce programme est python3 test.py. Le programme appelé est python3, et ce programme interprète le fichier test.py.

NB : time mesure le temps d'exécution d'une commande (ici 22s)

image-3.png

Code C :¶

image.png

NB : printf("%d", somme) permet d'afficher l'entier somme comme une chaîne de caratères.

Compilation : le programme gcc transforme le fichier test.c en fichier exécutable test.

Exécution : on mesure le temps d'exécution du programme test (ici 1,13s)

image-3.png

  • Sur cet exemple, le programme écrit en C est presque 20 fois plus rapide que le programme écrit en Pyton

Paradigme impératif¶

Un paradigme est une façon de se représenter les choses de manière cohérente.

La "philosophie" dans laquelle le langage C a été conçu en fait un langage dit impératif structuré : on donne des instructions/ordres à la machine qui les exécute séquentiellement.

A ce titre, on retrouve en C les notions de :

  • variable et affectation
  • instruction et séquence d'instructions
  • structures conditionnelles : branchement if/else, switch (H.P.)
  • structures itératives : boucles for et while

NB : on retrouve ces aspects "impératifs" dans la plupart des autres langages.

III. Le langage C, en bref¶

NB : en MP2I/MPI, on se restreint à un sous-ensemble du C (comme en OCaml)

III.1 Structure générale du langage¶

La plupart des langages de programmation sont construits de manière similaire ...

Le coeur du langage.¶

Tout ce qui est utilisable directement par le programmeur :

  • les types de base,

  • la syntaxe générale pour écrire une fonction, une boucle ...

    • cf la suite ...

Les bibiliothèques fournies avec le langage.¶

(library en anglais) ou modules

  • Ce sont des ensembles cohérents de fonctions / constantes / types.

  • Elles sont utilisables quasi-directement par le programmeur.

En Python : les modules math, random ... accessibles avec import

En C : la bibliothèque standard du C (lib c) accessibles avec #include

En OCaml : la Stdlib

Les bibliothèques externes¶

ou modules externes, ou packages (ensemble de modules), non fournis avec le langage.

Ecrits par d'autres programmeurs, il faut les installer en plus pour pouvoir les utiliser.

En Python : les modules numpy, matplotlib ...

En C : ... (opengl, sdl ...)

En OCaml : ... (graphics, str ...)

Et pour gérer les nombreuses bibliothèques externes ...¶

Pour la plupart des langages, la communauté des programmeurs s'organise pour faciliter l'utilisation de ces modules externes :

  • En Python : PyPI : Python Package Index

  • En Ocaml : Opam : Ocaml Package Manager

  • En C : rien ...

La bibliothèque standard C :¶

on accède aux fonctions de la librairie standard en mettant en début du fichier une ou plusieurs directives #include <...>

  • stdio.h : pour accéder aux fonctions entrées / sorties

  • math.h : pour accéder aux fonctions mathématiques

  • assert.h : pour accéder à la fonction assert

...

NB : .h comme header (en-tête) car un tel fichier ne contient pas la définition complète de ces foctions, mais uniquement leur signature (en-tête) (cf la suite)

In [15]:
// Exemple fonction de math.h
#include <stdio.h>
#include <math.h>

int main(){
    int a = pow(2,10); // fonction puissance de math.h
    printf("%d", a);
    return 0;
}
1024
In [18]:
//Exemple fonction assert
#include <stdio.h>
#include <assert.h>

int main(){
    printf("avant\n");   // affiché
    assert(1+1 == 3); // fonction assert de assert.h
    printf("après\n");  // non affiché
    return 0;
} 
tmp0vnublly.out: /tmp/tmprio328my.c:6: main: Assertion `1+1 == 3' failed.
avant
[C kernel] Executable exited with code -6

III.2 Les types de base :¶

NB : types de base ou types scalaires

  • entiers "signés" (négatifs ou positifs) : int ...
  • entiers "non-signés" (positifs uniquement) : unsigned int ...
  • réels ou flottants : float ...

    • ex : -1.3 $\quad$ 4. $\quad$ -3.317e8
  • caractères : char

    • ex : 'a' $\quad$ 'A' $\quad$ '\n'
  • booléens : ... (en fait, codés avec ces entiers)

III.3 Affichage avec printf :¶

La fonction printf permet d'afficher des informations sur la sortie standard (stdout, cf cours Linux).

utilisation : printf(chaine_de_formatage, arg1, arg2, ...)

  • printf est une fonction à nombre variable d'arguments.

  • la chaîne de formatage est le permier argument passé à la fonction. C'est une chaîne de caractères contenant des %x qui seront remplacés par les valeurs des autres arguments (dans l'ordre).

  • Attention, il faut tenir compte des types :

    • %d : affichage décimal d'un entier
    • %f : affichage décimal d'un flottant
    • %c : affichage d'un caractère unique
    • %s : affichage d'une chaîne de caractères
    • ...

NB : et ne pas oublier #include <stdio.h>

In [33]:
// Exemple printf et chaine de formatage
#include <stdio.h>

int main(){
    int i = -3;
    unsigned int n = 12;
    float x = 2.15e-3; 
    char lettre = 'a';
    
    printf("i=%d n=%d x=%f lettre=%c", i, n, x, lettre);

    return 0;
}
i=-3 n=12 x=0.002150 lettre=a
In [11]:
//Attention, aucun contrôle sur les types

#include <stdio.h>

int main(){
    int i = 99;
    
    printf("i=%d i=%c i=%f", i, i, i);

    return 0;
}
i=99 i=c i=0.000000

III.4 Opérations sur les types de base¶

Opérateurs numériques :¶

  • * + - / : sur les types numériques

NB : pas d'opérateur puissance (cf. fonction pow)

NB : conversions automatiques si les opérandes sont de types différents

Attention : la division sur les entiers est la division entière. En effet, les opérandes ne sont pas de types différents ...

  • % : sur les types entiers
In [14]:
// Illustration des conversions automatiques

#include <stdio.h>

int main(){
    printf("%d %f %f", 7/2, 7/2.0, 3*2.1);
    return 0;
}
3 3.500000 6.300000

Opérateurs de comparaison :¶

  • == != < > <= >= :

NB : les caractères sont comparables

image.png

Opérateurs booléens :¶

  • ! && || : non, et, ou

NB : évaluation paresseuse

III.3 Les variables :¶

En C :

  • une variable = un identifiant (nom) + un type + une valeur

III.3.1 Les variables en Python :¶

En Python, le type est associé à la valeur :

  • les types ne sont pas écrits dans le code

  • une variable peut changer de type au cours de l'exécution

  • la vérification de la cohérence des types se fait pendant l'exécution (typage dynamique)

In [2]:
## Code Python : variables et types ##
v = 4
print(type(v)) # v de type 'int'

v = [2]
print(type(v)) # v de type 'list'

print(2+[]) # erreur de type à l'exécution
<class 'int'>
<class 'list'>
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-2-795a53079250> in <module>
      6 print(type(v)) # v de type 'list'
      7 
----> 8 print(2+[]) # erreur de type à l'exécution

TypeError: unsupported operand type(s) for +: 'int' and 'list'

image.png

III.3.2 Les variables en C:¶

En C, le type est porté par l'identifiant:

  • le type d'une variable est systématiquement écrit dans le code

  • ce type correspond à une taille mémoire réservée pour cette variable

  • une variable ne peut pas changer de type

  • la vérification de la cohérence des types se fait avant l'exécution (typage statique), mais n'empêche pas forcément l'exécution (typage faible)

Définition / initialisation de variable¶

In [3]:
// Exemple de définition / initialisation de variables

#include <stdio.h>

int main(){
    int x,y; // définition de deux variables entières non initialisées
    int z=0,t; // définition de deux autres variables, la première étant initialisée
    x = 1; y =2; t=3;
    printf("%d %d %d %d", x, y, z, t);
    return 0;
}
1 2 0 3

Taille mémoire associée à un type¶

Pour connaître la taille d'un type T, on utilise sizeof(T) qui donne le nombre d'octets occupés par une variable de type T.

In [16]:
// taille mémoire associée à un type

#include <stdio.h>

int main(){
    printf("%d %d %d", sizeof(int), sizeof(char), sizeof(float));
    return 0;
}
4 1 4
  • 4 octets pour un int
  • 1 octet pour un char
  • 4 octets pour un float

Détection d'erreur de type avant l'exécution¶

En C, c'est le compilateur qui réalise la vérification de types

  • certaines erreurs sont fatales : le code ne s'exécutera pas

  • certaines erreurs ne le sont pas : un avertissement est affiché (warning), mais le code s'exécute tout de même, et souvent plante ...

In [1]:
// Erreur de type ... non fatale, malheureusement ...
#include <stdio.h>
int main(){
    printf("avant\n");
    printf(3);  // printf ne s'applique pas à un entier ...
    printf("après\n");
    return 0;
}
/tmp/tmpfjd03vpv.c: In function ‘main’:
/tmp/tmpfjd03vpv.c:5:12: warning: passing argument 1 of ‘printf_wrap’ makes pointer from integer without a cast [-Wint-conversion]
    5 |     printf(3);  // printf ne s'applique pas à un entier ...
      |            ^
      |            |
      |            int
In file included from /tmp/tmpfjd03vpv.c:2:
/home/julien/jupyter-c-kernel/jupyter_c_kernel/resources/stdio_wrap.h:78:29: note: expected ‘const char *’ but argument is of type ‘int’
   78 | int printf_wrap(const char *format, ...) {
      |                 ~~~~~~~~~~~~^~~~~~
avant
[C kernel] Executable exited with code -11

Typage (faible) en C¶

Le langage C est très (trop) souple avec les types.

Il réalise des conversions automatiques si les types ne sont pas respectés.

Ces conversions sont parfois pertinentes et voulues par un programmeur C expérimenté. Mais elles sont souvent conséquences d'erreurs de programmation et à l'origine de nombreux bugs ...

Ici, l'entier 3 est converti automatiquement en chaîne de caractères (qui n'est pas la chaine "3") ... et ce code grossièrement faux s'exécute (et plante !)

Les messages d'avertissement en C :¶

Les messages d'avertissement correspondent très souvent à des erreurs du programmeur¶

Ils doivent donc :

  • être lus
  • considérés avec attention
  • et, pour la plupart d'entre eux, le code doit être corrigé

... sous peine d'avoir un code qui plante sans raison apparente (ce qui est fort désagréable ...)

Variable locale / variable globale¶

  • Une variable définie en dehors des fonctions est dite variable globale

  • Une variable définie à l'intérieur d'une fonction est dite variable locale

NB : comme en Python

image.png

Portée des variables¶

En C, les portées sont délimitées par les accolades.

In [23]:
// Erreur portée de variable

#include <stdio.h>

int main(){
    {
        int i = 0;
        printf("%d\n", i);
    }
    printf("%d\n", i); // i hors de portée ... erreur détecté avant l'exécution
    return 0;
}
/tmp/tmpa3yk2r6w.c: In function ‘main’:
/tmp/tmpa3yk2r6w.c:10:20: error: ‘i’ undeclared (first use in this function)
   10 |     printf("%d\n", i); // i hors de portée ... erreur détecté avant l'exécution
      |                    ^
/tmp/tmpa3yk2r6w.c:10:20: note: each undeclared identifier is reported only once for each function it appears in
[C kernel] GCC exited with code 1, the executable will not be executed
In [27]:
// Exemple portée de variable

#include <stdio.h>

int main(){
    int i = 1;
    {
        int i = 0;         // nouveau i dans une nouvelle portée
        printf("%d\n", i);
    }
    printf("%d\n", i); // i dans la portée actuelle
    return 0;
}
0
1
In [26]:
// Exemple portée de variable

#include <stdio.h>

int main(){
    int i = 1;
    {        
        printf("%d\n", i); // on peut accéder aux variables des portées englobantes
    }
    printf("%d\n", i);
    return 0;
}
1
1
In [24]:
// Erreur portée de variable

#include <stdio.h>

int main(){
    int i = 1;
    int i = 0;  // interdiction de définir deux variables de même nom dans la même portée
    return 0;
}
/tmp/tmpfkw64gyi.c: In function ‘main’:
/tmp/tmpfkw64gyi.c:7:9: error: redefinition of ‘i’
    7 |     int i = 0;
      |         ^
/tmp/tmpfkw64gyi.c:6:9: note: previous definition of ‘i’ was here
    6 |     int i = 1;
      |         ^
/tmp/tmpfkw64gyi.c:7:9: warning: unused variable ‘i’ [-Wunused-variable]
    7 |     int i = 0;
      |         ^
[C kernel] GCC exited with code 1, the executable will not be executed

les variables "constantes"¶

NB: oxymore (!)

On peut les précéder du mot-clé const:

  • on protège la variable d'un malencontreuse erreur de programmation

  • peut permettre au compilateur de générer un code machine un peu plus efficace

In [30]:
// Erreur tentative de modification d'un variable const

#include <stdio.h>

int main(){
    const int x = 0;
    x = 3;  // tentative de modification
    printf("%d",x);
    return 0;
}
/tmp/tmppgfsh5yo.c: In function ‘main’:
/tmp/tmppgfsh5yo.c:7:7: error: assignment of read-only variable ‘x’
    7 |     x = 3;  // tentative de modification
      |       ^
[C kernel] GCC exited with code 1, the executable will not be executed

III.4 Les fonctions :¶

Pour une fonction, les types des paramètres et le type de la valeur de retour doivent être indiqués.

(Même principe que pour les variables : en C, on indique les types)

NB :

  • fonctions récursives possibles
  • possibilité de ne rien renvoyer : type void
  • possibilité de déclarer une fonction (donner sa signature) et de la définir par la suite
  • pas de paramètres nommés, pas de paramètres avec valeur par défaut ...
  • il est possible de passer en paramètre une fonction à une fonction (HP)
  • il est possible de définir des fonctions avec un nombre variable de paramètres (HP)
In [50]:
// Exemple de fonction

#include <stdio.h>

int somme(int a, int b){
    return a+b;
}

int main(){
    int s = somme(2,3);
    int t = somme(3, somme(3,3));
    printf("s=%d t=%d\n",s,t);
    return 0;
}
s=5 t=9
In [57]:
// Exemple de fonction sans valeur de retour

#include <stdio.h>

void welcome(){
    printf("Bienvenue dans le jeu du taquin\n\n");
    printf("Les règles du jeu sont les suivates:\n");
    printf("...");
}

int main(){
    welcome();
    return 0;
}
Bienvenue dans le jeu du taquin

Les règles du jeu sont les suivates:
...
In [56]:
// Exemple de séparation déclaration / définition

#include <stdio.h>

/* Déclaration de toutes les fonctions utiles */

int somme(int a, int b); // déclaration de la fonction somme


/* Fonction principale */

int main(){
    int s = somme(2,3);
    printf("s=%d\n",s);
    return 0;
}

/* Implémentation des fonctions utiles */

int somme(int a, int b){ // définition de la fonction somme
    return a+b;
}
s=5

III.5 Les structures de contrôle¶

III.5.1 if - if else¶

if (cond) {
   ...
}

ou bien

if (cond) {
   ...
} else {
   ...
}

NB : parenthèses obligatoires, accolades facultatives

NB : pas de elif : on imbrique

In [73]:
// Exemple : if imbriqués

#include <stdio.h>  // VERSION UN PEU LOURDE

int nbracines(float a, float b, float c){
    float delta = b*b-4*a*c;
    if (delta == 0.0) {
        return 1;
    } else {
        if (delta > 0.0) {
            return 2;
        } else {
            return 0;
        }
    }
}

int main(){
    printf("%d\n",nbracines(1,2,1));  // X^2 + 2 X + 1 : 1 racine réelle
    printf("%d\n",nbracines(1,1,1));  // X^2 + X + 1 : 0 racine réelle
    printf("%d\n",nbracines(1,-3,2)); // x^2 - 3X + 2 : 2 racines réelles
    return 0;
}
1
0
2

NB : si 1 seule instruction entre accolades, on peut se passer des accolades

In [39]:
#include <stdio.h>  // VERSION DECONSEILLEE

int nbracines(float a, float b, float c){
    float delta = b*b-4*a*c;
    if (delta == 0.0)
        return 1;
    else 
        if (delta > 0.0) {
            return 2;
        } else {
            return 0;
        }
}


int main(){
    printf("%d\n",nbracines(1,2,1));  // X^2 + 2 X + 1 : 1 racine réelle
    printf("%d\n",nbracines(1,1,1));  // X^2 + X + 1 : 0 racine réelle
    printf("%d\n",nbracines(1,-3,2)); // x^2 - 3X + 2 : 2 racines réelles
    return 0;
}
1
0
2

En poursuivant :

In [40]:
#include <stdio.h> // VERSION DECONSEILLEE

int nbracines(float a, float b, float c){
    float delta = b*b-4*a*c;
    if (delta == 0.0)
        return 1;
    else if (delta > 0.0)
        return 2;
    else
        return 0;
}


int main(){
    printf("%d\n",nbracines(1,2,1));  // X^2 + 2 X + 1 : 1 racine réelle
    printf("%d\n",nbracines(1,1,1));  // X^2 + X + 1 : 0 racine réelle
    printf("%d\n",nbracines(1,-3,2)); // x^2 - 3X + 2 : 2 racines réelles
    return 0;
}
1
0
2

Et même :

In [36]:
#include <stdio.h>

int nbracines(float a, float b, float c){
    float delta = b*b-4*a*c;
    if (delta == 0.0) return 1;
    else if (delta > 0.0) return 2;
    else return 0;
}


int main(){
    printf("%d\n",nbracines(1,2,1));  // X^2 + 2 X + 1 : 1 racine réelle
    printf("%d\n",nbracines(1,1,1));  // X^2 + X + 1 : 0 racine réelle
    printf("%d\n",nbracines(1,-3,2)); // x^2 - 3X + 2 : 2 racines réelles
    return 0;
}
1
0
2

ou encore (trop peu lisible) :

In [41]:
#include <stdio.h> // VERSION A EVITER

int nbracines(float a, float b, float c){
    float delta = b*b-4*a*c;
    if (delta == 0.0) return 1; else if (delta > 0.0) return 2; else return 0;
}


int main(){
    printf("%d\n",nbracines(1,2,1));  // X^2 + 2 X + 1 : 1 racine réelle
    printf("%d\n",nbracines(1,1,1));  // X^2 + X + 1 : 0 racine réelle
    printf("%d\n",nbracines(1,-3,2)); // x^2 - 3X + 2 : 2 racines réelles
    return 0;
}
1
0
2

Version conseillée pour les cas disjoints :¶

on préserve les accolades (programmation défensive)

In [42]:
#include <stdio.h> // VERSION CONSEILLEE

int nbracines(float a, float b, float c){
    float delta = b*b-4*a*c;
    if (delta == 0.0) {
        return 1; 
    } else if (delta > 0.0) {
        return 2; 
    } else {
        return 0;
    }
}


int main(){
    printf("%d\n",nbracines(1,2,1));  // X^2 + 2 X + 1 : 1 racine réelle
    printf("%d\n",nbracines(1,1,1));  // X^2 + X + 1 : 0 racine réelle
    printf("%d\n",nbracines(1,-3,2)); // x^2 - 3X + 2 : 2 racines réelles
    return 0;
}
1
0
2

sans accolades : un bug fréquent ...¶

In [1]:
#include <stdio.h> // CODE FAUX

int nbracines(float a, float b, float c){
    float delta = b*b-4*a*c;
    if (delta == 0.0)
        printf("TEST");
        return 1;
    else if (delta > 0.0)
        return 2;
    else
        return 0;
}


int main(){
    printf("%d\n",nbracines(1,2,1));  // X^2 + 2 X + 1 : 1 racine réelle
    printf("%d\n",nbracines(1,1,1));  // X^2 + X + 1 : 0 racine réelle
    printf("%d\n",nbracines(1,-3,2)); // x^2 - 3X + 2 : 2 racines réelles
    return 0;
}
/tmp/tmp4ax2giop.c: In function ‘nbracines’:
/tmp/tmp4ax2giop.c:8:5: error: ‘else’ without a previous ‘if’
    8 |     else if (delta > 0.0)
      |     ^~~~
[C kernel] GCC exited with code 1, the executable will not be executed

L'indentation n'est pas signifiante en C !

D'où l'intérêt de mettre systématiquement des accolades

Version possible, en cas d'instruction unique :¶

  • on peut la mettre sur la même ligne que la condition
In [58]:
#include <stdio.h> // VERSION POSSIBLE

int nbracines(float a, float b, float c){
    float delta = b*b-4*a*c;
    if (delta == 0.0) return 1;
    else if (delta > 0.0) return 2;
    else return 0;
}

int main(){
    printf("%d\n",nbracines(1,2,1));  // X^2 + 2 X + 1 : 1 racine réelle
    printf("%d\n",nbracines(1,1,1));  // X^2 + X + 1 : 0 racine réelle
    printf("%d\n",nbracines(1,-3,2)); // x^2 - 3X + 2 : 2 racines réelles
    return 0;
}
1
0
2

III.5.2 boucle while¶

while (cond) {
   ...
}

NB :

  • break (sortie anticipée), continue (passage au tour suivant)

  • parenthèses obligatoires

  • si 1 seule instruction entre accolades, on peut se passer des accolades

In [62]:
// Exemple while

#include <stdio.h>

int main(){
    int i = 9;
    while (i>0) {
        printf("%d",i);
        i -= 1;
    }
    return 0;
}
987654321

Opérateurs d'incrémentation et autres ...¶

  • x += 10 signifie x = x+10

  • -= *= /= %= ...

Déconseillé en MPI (mais très utilisé par les programmeurs C)¶

  • x++ signifie x += 1 (++x également)

  • x-- signifie x -= 1 (--x également)

III.5.3 boucle for¶

for (init; expr; incr) {
   ...
}

c'est une while déguisée !

init;
while (expr) {
   ...
   incr;
}

NB : for (init; expr; incr)

  • possibilté de définir une variable dans init

  • break (sortie anticipée), continue (passage au tour suivant)

  • si 1 seule instruction entre accolades, on peut se passer des accolades

    • mêmes remarques que pour if et while ; mieux vaut laisser les accolades
In [63]:
// Exemple for

#include <stdio.h>

int main(){
    for (int i=0; i<10; i++) {
        printf("%d",i);
    }
    return 0;
}
0123456789
In [66]:
// Exemple for

#include <stdio.h>

int main(){
    for (int i=9; i>0; i--) printf("%d",i);
    return 0;
}
987654321
In [72]:
// Exemple for imbriquées

#include <stdio.h>

int main(){
    for (int i=0; i<3; i++) {
        for (int j=0; j<2; j++){
            printf("(i=%d j=%d) ",i,j);   
        }
    }
    return 0;
}
(i=0 j=0) (i=0 j=1) (i=1 j=0) (i=1 j=1) (i=2 j=0) (i=2 j=1) 

A suivre ...¶