Informatique et recherche
opérationnelle
IFT2030

Concepts des langages de programmation

Automne 2002


Accueil

Professeur et
Démonstrateurs

Horaires et locaux

Notes de cours

Démonstrations
et devoirs

Examens

Liens utiles

Au sujet du
livre de cours

Consultez vos notes

Foire aux questions
(FAQ)

Démonstration 1: Introduction légère au langage C


COMMENT COMPILER

  Deux compilateurs: cc et gcc
  gcc -Wall -g foo.c -o foo
    -Wall: affiche tous les messages d'avertissement
    -g: produit du code interprétable par le débogueur, gdb
    -o foo: nomme l'exécutable foo (par défaut: a.out)

TYPES DE BASE

  char  int  float  double

  Note:
    Pas de booléen! On utilise les entiers(0 = = faux, n'importe quoi d'autre == vrai)

  unsigned: unsigned int, unsigned char
  short et long: short int (short), long int (long)


DÉCLARATION DE VARIABLES

  int a;
  double b, c;

  Note:
    Les variables automatiques définies en C ne sont pas implicitement initialisées.


TYPES COMPLEXES

Tableaux:
  int tab[5];
  tab[0] = 0;
  tab[1] = 1;
  ...
  tab[4] = 4;
  tab[5] = 5; /* ILLÉGAL(Compile, mais attention aux segmentation faults!) */

  int mat[2][3]
  mat[0][2] = 0;

  int tab[]; /* Légal, mais on ne peut accéder aux éléments du tableau
                avant d'en avoir spécifié la taille */

Pointeurs:
  int *p;

  Le pointeur nul est identifié par NULL. Ex: if (p == NULL) ...

Structures:
  struct point {
    char etiquette;
    int x, y;
  };

  struct point A;
  A.x = 0;

  struct point *p_A;
  p_A->x = 0;  ou  (*p_A).x = 0;

Chaînes de caractères:
  Une chaîne de caratères est un tableau de char terminé par le char '\0'

  char mot1[5];  /* Réserve cinq "cases" en mémoire */
 char *mot2;    /* Ne réserve pas de mémoire */


CONSTANTES

  1 10 -5      int
 100L         long  (la plupart des compilateurs ont int=long)
 1.2 1.0e-8   float
 1.2L         double
 'c' '\n'     caractère
 "hello"      réserve 6 cases en mémoire, initialise le contenu avec hello
              et retourne un pointeur vers ces cases mémoire.

ARITHMÉTIQUE

  Opérateurs: + - * / %(modulo)
  Opérations sur les bits: & | ^ ~ >> <<
  Auto-incrémentation: ++ --
   ++x ou x++   => x=x+1
Important:
   ++x == x+1
   x++ == x
  Avec affectations: += -= *= /= %= &= |= ^=
   x op= y  <==>  x = x op y

AFFECTATIONS

  a=2;
  a=X && Y;
  *(get_ptr())=3;
  1+1=2;     /* Illégal, le membre de gauche doit être une l-value(espace mémoire) */

DÉCLARATION DE FONCTIONS ET PROCÉDURES

  int inc(int a)
  {
    return ++a;
  }

  void foo()
  {
    printf("foo");
  }


VARIABLES LOCALES, GLOBALES, AUTOMATIQUES, STATIQUES, CONSTANTES

  auto int x;  /* Équivalent à int x; */
  const int b=2;
  int foo(const int c,int d)
  {
    int e;
    static int f=2;
    while(1)
      {
        int g=2,a=10;
        if(!a) break; else a-=g;
        e++;
      }
    return b+e;
  }
    -Les variables locales ne sont utilisables que dans le corps les englobants.
    Locales: e,f,g,a
    -Les variables globales sont utilisables partout dans le programme.
    Globales: x,b
    -Les variables automatiques allouent et détruisent leur espace dynamiquement, en utilisant en général la pile.
     De plus, l'initialisation est faite à chaque fois que l'espace est alloué.
    Automatiques: x,e,g,a
    -Les variables statiques ont leur espace mémoire propre et définitif dès le début du programme et ne sont
     initialisées qu'une seule fois. Une variable statique locale à l'avantage de garder sa valeur au fil des appels,
     tout en restant invisible à l'extérieur du corps l'englobant.
    Statique: f
    -Les "variables" constantes ont la propriété de ne pas pouvoir être modifiées.
    Constante: b


TYPEDEF

  typedef int *ptr_int;
  typedef int frequences[256];
  typedef struct LISTE_DE_CONTEXTES liste_de_contextes;
  struct LISTE_DE_CONTEXTES {
    contexte *element;
    liste_de_contextes* prochain;
  };

Variante:
  typedef struct LISTE_DE_CONTEXTES {
    contexte *element;
    struct LISTE_DE_CONTEXTES *prochain;
  } liste_de_contextes;

Utilisation des types:
  ptr_int p;
  frequences f;
  liste_de_contextes *l;


EXPRESSIONS BOOLÉENES

  !      Opérateur unaire NON
  &&     Opérateur binaire ET
  ||     Opérateur binaire OU
  ==     Operateur binaire d'égalité numérique
  !=     Raccourci: (x!=y) == !(x==y)

NOTE SUR LES && ET LES ||

  Une expression contenant && ou || est évaluée seulement jusqu'à ce
  qu'on en connaisse la valeur de vérite. Par exemple, dans
  l'expression:
  if (1 || x > 0) ...
      x > 0 n'est jamais évalué. De même pour
  if (0 && x > 0) ...
      Utile pour faire:
  if ((l != NULL) && (l->element == ...)) ...
  Remarquez aussi que NULL a la valeur de vérité faux, et donc
  l'expression précédente peut s'ecrire
  if (l && (l->element == ...)) ...

ÉNONCÉS DE CONTRÔLE

  if (<expression>) <instruction>
  if (<expression>) <instruction> else <instruction>

  if (a<5) a = 0;
  if (a<5)
     a = 0;
  if (a<5) {
     a = 0;
     b = 1;
  }
  else {
     a = 10;
     b = 11;
  }

Note:
  Aussi possible: a = a<5 ? 0 : 10;

  while (<expression>) <instruction>

  while (a>0) {
     printf("*");
     a--;
  }
 

  do <instruction> while (<expression>);

  do {
     printf("*");
     a--;
  } while (a>0)
 

  for (<init>; <test>; <inc>) <instruction>

  /* Équivalent à: */
     <init>
     while (<test>) {
        <instruction>
        <inc>
     }

   for (i=0; i<tablen; i++)
      tab[i] = 0;


BREAK ET CONTINUE

  break, utilisé dans un while, do, for ou switch, fait sortir de la boucle.

  continue retourne au début de la boucle. Dans le cas d'un while, on
  teste immédiatement la condition d'arrêt de la boucle. Dans le cas
  d'un for, on exécute d'abord l'incrémentation.


SWITCH

  switch (note) {
    case 'A': case 'B': case 'C': case 'D':
       ++nb_reussite;
       break;
    case 'E': case 'F':
       ++nb_echecs;
       break;
    default:
       printf("note non reconnue");
       break;
   }

EXIT

  void exit(int status);

  Met fin à l'exécution en envoyant le signal status. On peut
  aussi utiliser un return si on est dans le main().


ENTRÉES/SORTIES DE BASE

   stdin, entrée standard, normalement le clavier
   stdout, sortie standard, normalement l'écran
   stderr, erreur standard, normalement l'écran

   sous UNIX, tp1 -n 3 < entree.txt > sortie.txt
   => stdin = le fichier entree.txt
   => stdout = le fichier sortie.txt
   => stderr demeure l'écran

   Redirection de stderr:
   tp1 -n 3 < entree.txt > sortie.txt 2> erreurs.txt

   La fonction printf écrit au stdout.
   printf("foo");

  float entropie;
  printf("L'entropie est %f", entropie);

  fprintf(FILE *fp, ...) /* fait comme printf, mais écrit dans le fichier fp. */

Exemple:
  fprintf(stderr,"Erreur, mauvais parametre.\n");

Les spécificateurs principaux:
    %d    imprime un int.
    %6d   imprime un int  sur 6 espaces.
    %f    imprime un float.
    %6.2f imprime un float sur 6 espaces dont 2 après le point.
    %c    imprime un char.
    %s    imprime un string (char *).

Important:
   Pas de typecheck sur les arguments à imprimer!

Caractères speciaux:
    \n    newline
    \t    tab


PRÉPROCESSEUR

Inclusion de sources:
  #include <stdio.h>   /* Cherche stdio.h dans des répertoires prédefinis */
  #include "debug.h"   /* Cherche debug.h dans le répertoire courant */

Les principales librairies de fonctions:
  stdio.h   routines d'entrées/sorties
  stdlib.h  fonctions et constantes diverses (exit, NULL, ...)
  string.h  manipulation de chaînes de caractères
  math.h    fonctions mathématiques

Constantes et macros:
  #define MAX_TABLE 10
    /* MAX_TABLE devient un synonyme de 10 */
  #define MAX(a,b) ((a) > (b) ? (a) : (b))
    /* b = MAX(c,d/2);  =>  b = ((c) > (d/2) ? (c) : (d/2)); */

  La convention est d'utiliser des majuscules pour les noms des macros
  pour les différencier des fonctions.
  Remarquez les parenthèses autour de a et b. Sans elles, on pourrait
  avoir des problemes.
  Exemple:
    #define MAX(a,b) a > b ? a : b
    b = MAX(2,3)*5  =>  b = 2 > 3 ? 2 : 3*5
  Ou, en plus simple,
    #define MUL(a,b) a * b
    MUL(1 + 2, 3);  =>  1 + 2 * 3;
  ce qui donne 7 plutôt que le 9 voulu.


MANIPULATION DE CHAÎNES DE CARACTÈRES

  Quelques fonctions définies dans string.h:

 strcpy(char *destination, char *source);
  copie source dans destination.
  !!!ATTENTION: destination doit être assez grand pour pouvoir
  contenir source. Le compilateur et la fonction ne font aucune
  vérification, c'est au programmeur de s'assurer que la taille de
  destination est suffisante.
  Ex:
     char ch1[5];
     strcpy(ch1, "foo");     /* OK */
     strcpy(ch1, "foobar");  /* incorrect */
     strcpy(ch1, "fooba");   /* incorrect, car il ne reste plus d'espace
                                dans ch1 pour le caractère '\0' */

 strncpy(char *destination, char *source, int n);
   /* Copie les n premiers caractères de source dans destination.*/

  strlen(char *string);
    /* Retourne la longueur de la chaîne, sans compter le '\0'  */
    Ex:
    strlen("foo") == 3;

  strcat(char *s1, char *s2);
    /* Concatène s2 à s1. Le premier caractère de s2 écrase le '\0' à la fin de s1. */
    Ex:
     char ch1[20];
     strcpy(ch1, "foo");
     strcat(ch1, "bar");
       /* => ch1 == foobar */

  int strcmp(char *s1, char *s2);
  /* retourne <0 si s1 < s2
               0 si s1 == s2
              >0 si s1 > s2  */


Travail pratique (pas noté mais fortement recommandé)

1) Implémentation de quelques fonctions de la librairie string.h

Implementez (pas nécessairement avec des pointeurs) les fonctions: strlen, strcpy et strcat

2) Tri d'un tableau d'entiers

  1. Écrire une méthode qui affiche un à un les éléments d'un tableau d'entiers passé en paramètre.
    Voici le squelette (beurk) de cette méthode, où nb indique le nombre d'éléments à afficher:
    void affiche(int tab[], int nb) {
      /* a vous de completer */
    }
    
  2. Écrire une méthode qui permet de trier (en ordre croissant) un tableau d'entiers passé en paramètre.
    Voici le squelette (beurk) de cette méthode, où nb indique le nombre d'éléments à trier:
    void tri(int tab[], int nb) {
      /* a vous de completer */
    }
    

Voici un exemple d'utilisation de cette méthode:

#include <stdio.h> /* entree/sortie standard */

/*
 * mettre ici votre code pour les méthodes affiche et tri
 */

int tab1[] = {12, 15, 5, 8, 10};
int tab2[] = {10, 15, 12};

int main(int argc, char *argv[]) { 
	
  printf("avant "); affiche(tab1,5);
  tri(tab1,5); 
  printf("apres "); affiche(tab1,5);

  printf("avant "); affiche(tab2,3);
  tri(tab2,3); 
  printf("apres "); affiche(tab2,3);

  return 0; /* signale que tout va bien */
}                        

3) Tri d'un tableau de structures

Soit la structure:
typedef struct {
  char nom[100];
  int age;
} Personne;
Dans cette exercice, on suppose que les tableaux de Personne ont pour dernier élément, une personne factice dont l'âge est 0. Ce dernier élément est simplement un marqueur de fin de table.
Exemple:
Personne tab[] = {
 {"jean",27},
 {"valérie",23},
 {"fabienne",23},
 {"paul",23},
 {"isabelle",24},
 {"nop",0} /* marqueur de fin de tableau (a ne pas afficher) */
};
  1. Écrire une méthode qui permet d'afficher un tableau de Personne. On ne passera pas la taille du tableau comem dans l'exercice précédant.
  2. Écrire une méthode qui permet de trier un tableau de Personne passé en paramètre en ordonnant les personnes de la plus jeune à la plus vieille.
  3. Écrire une méthode qui permet de trier un tableau de Personne passé en paramètre en ordonnant les personnes en fonction de leur nom.
  4. Écrire une méthode qui permet de trier un tableau de Personne passé en paramètre en ordonnant les personnes en fonction de leur âge (premier critère) et de leur nom (deuxième critère à appliquer lorsque deux personnes ont le même âge).

    En clair, voici l'ordre des éléments après application de ce tri pour le tableau donné en exemple:

     {"fabienne",23}, {"paul",23}, {"valérie",23}, {"isabelle",24}, {"jean",27}, {"nop",0} 
    

4) Un peu plus difficile

Note: cet exercice peut-être évité de tous ceux qui programment en C pour la première fois.

Vous devez en avoir marre de réécrire une méthode de tri pour chaque critère de tri.
Consultez le manuel en ligne (man qsort) de manière à utiliser la méthode qsort.

[top]


Dernière mise à jour : Sun Sep 15 21:49:57 EDT 2002
dift2030@iro.umontreal.ca