Sven De Félice et Jean-Jacques BOURDIN

Université Paris 8
Dépt. Programmation et Informatique Fondamentale,
2, rue de la Liberté
93526 Saint-Denis Cedex
FRANCE


Programmation Impérative
Imperative Programming

Spring 2022


Overview

Ch I) Let‘s try!

Ch II) Easy C

    II.A) Variables, Expressions, Types, Instructions
    II.B) Operators
    II.C) Control Flow
    II.D) Data Structures
    II.E) Compilation

Ch III) C Addresses

Ch IV) C more than that

    IV.A) Compilation

    IV.B) Files

    IV.BC Arithmetics



Quelques précisions pour la fin de semestre de printemps 2022.

Le dernier partiel écrit aura lieu le 20/4/2022 de 13h à 15h en salle B104.



 

 

 

 

 

 

 

 

 

 


Ch IV) What else?

    IV.A) Compiler
Règles de Compilation

  • The four steps for compiling
    Les quatre étapes de la compilation
  • The preprocessor

    It obeys to orders starting with #. Examples:
    Les directives qui commencent par # et elles seules sont évalluées par le précompilateur. Il les exécute. Parmi ces directives nous verrons essentiellement les :

    Pseudo-constants and pseudo-functions
    pseudo-constantes et pseudo-fonctions
    Il s'agit de remplacer dans le texte, dans le corps du programme, toute occurence du mot défini par sa définition. Exemple :
    #define MAXI 100
    every occurence of MAXI will be replaced by 100.
    #include <stdio.h>
    #define GT(a, b) a < b ? b : a
    #define CARRE(a) a * a
    
    main () {
      int i, s;
      i = 10;
      s = 12;
      printf("Le MAX de %3d et %3D est %d\n",i,s,GT(i,s));
      printf("Le MAX de %3d et %3D est %d\n",i,s, GT(i++,s++));
      printf("Le MAX de %3d et %3D est %d\n",i,s, GT(i+1,s+1));
      printf("Le carre de %3d est %d\n",i + 1,CARRE(i + 1));
      printf("Le carre de %3d est %d\n",i,CARRE(i++));
      printf("Le carre de %3d est %d\n",i,CARRE(--i));
    }
    

    After preprocessoring, this part of the program becomes:
    après compilation partielle, devient
    main () {
      int i, s;
      i = 10;
      s = 12;
      printf("Le MAX de %3d et %3D est %d\n",i,s,i < s ? s : i);
      printf("Le MAX de %3d et %3D est %d\n",i,s, i++ < s++ ? s++ : i++);
      printf("Le MAX de %3d et %3D est %d\n",i,s, i+1 < s+1 ? s+1 : i+1);
      printf("Le carre de %3d est %d\n",i + 1,i + 1 * i + 1);
      printf("Le carre de %3d est %d\n",i,i++ * i++);
      printf("Le carre de %3d est %d\n",i,--i * --i);
    }
    

    The program does:
    dhcp2.ai.univ-paris8.fr: a.out
    Le MAX de  10 et  12 est 12
    Le MAX de  10 et  12 est 13
    Le MAX de  11 et  14 est 15
    Le carre de  12 est 23
    Le carre de  11 est 132
    Le carre de  13 est 132
    
    Et que diriez-vous de ce résultat paradoxal ?
    #define MAX(s,t) s < t ? t : s
    
    main () {
      int i, j, k, m, n;
      i = 2;
      j = 5;
      k = 6;
      n = MAX(i,j);
      m = MAX(n, k);
      printf("MAX(%3d,%3d,%3d) == %3d\n",i,j, k, m);
      printf("MAX(%3d,%3d,%3d) == %3d\n",i,j, k, MAX(MAX(i,j),k));
    }
    

    Ça a l'air tout simple et, presque, normal, non ? Essayons.
    jj@mu $ a.out
    MAX(  2,  5,  6) ==   6
    MAX(  2,  5,  6) ==   5
    jj@mu $ 
    

    ... qui n'est pas tout à fait normal.
    Let's look at the preprocessed code:
    jj@mu $ gcc -E ex.c
    # 1 "ex.c"
    # 1 ""
    # 1 ""
    # 1 "ex.c"
    
    
    main () {
      int i, j, k, m, n;
      i = 2;
      j = 5;
      k = 6;
      n = i < j ? j : i;
      m = n < k ? k : n;
      printf("MAX(%3d,%3d,%3d) == %3d\n",i,j, k, m);
      printf("MAX(%3d,%3d,%3d) == %3d\n",i,j, k, i < j ? j : i < k ? k : i < j ? j : i);
    }
    

    Just look at it and the error seems ovious.
    Cette fois je vois bien où est l'erreur, dans ce cas on regarde, si i est plus petit que j on revoie j, sinon on fait l'évaluation de i plus petit que k. C'est idiot, on voudrait justement le contraire !
  • Libraries
    Bibliothèques

    There are some very well documented standard libraries, you should check them on:
    On trouvera de la documentation précise sur les bibliothèques standard sur
    le lien suivant. Vous devriez en prendre connaissance.


        IV.A) Arithmétique


    1. Décalages
    2. Remarquons :
        Décimal       Binaire
            3              11
            6             110
           12            1100
           24           11000
           48          110000
           96         1100000
          192        11000000
            4             100
            8            1000
           16           10000
           13            1101
           26           11010
      
      Remarquons que, en décimal, 1230 et dix fois plus grand que 123. Donc, en binaire, 101010 et deux fois plus grand que 10101. L'opérateur de décalage à gauche
       << 
      permet donc de décaler un nombre binaire de n cases donc de le multiplier par 2 puissance n.
      void puiss2 (int pmax) {
        int i, p;
        p = 1;
        for (i=0; i < pmax; i++) {
          printf("%d\t",p);
          p = p << 1;
        }
        printf("\n");
      }
      

      Bien sûr le décalage à droite fait exactement le contraire.
      Attention, pour les nombres négatifs, comme vous le savez, pour que le nombre reste négatif, si le premier chiffre est un 1, au cours du déclage à droite, ce sont des 1 qui sont mis à gauche !
    3. Et binaire
    4.     01011010
      &   00011100
      ------------
          00011000
      
          01011010
      &   00000001
      ------------
          00000000
      
      S'écrit avec &.

      int imper (int i) {
        return i & 0x01;
      }
      

    5. Ou binaire
    6. int nonul (int i) {
        while (i) {
          if (i | 0x01)
             return 1;
          i = i << 1;
        }
        return 0;
      }
      /* completement inutile, non */
      

    7. Ou bien binaire
    8. int differ (int i, int j) {
         if (i ^ j)
            return 1;
         return 0;
      }
      

    9. Non binaire
    10. Essayez donc ceci :

      int inver (int i) {
        return 1 + ~i;
      }
      int main () {
        int n;
      
        for (n = -10; n < 11; n++)
      	printf("%d == -  %d\n", n, inver(n));
      }
      

        IV.B) Input/output
    Entrées/Sorties


    1. First example see in class on March the 25th 2020
      To write on a file one needs to open it and... to write on it.
      We start by the vector definition.
      #include <stdio.h>
      #include <stdlib.h>
      #include <assert.h>
      struct vect {
        int nbele;
        int * v;
      } ;
      typedef struct vect vect_t;
      
      Print such a vector is easy :
      void afficv(vect_t v) {
        int i;
        for (i = 0; i < v.nbele; i++)
          printf("%d ",v.v[i]);
        printf("\n");
      }
      
      Read from a file, first the number of elements then the elements is easy too, as soon as we have a file descriptor :
      vect_t lire (FILE * fd, int nbele) {
        vect_t v;
        int i, nbelefic, tmp;
        v.nbele = nbele;
        fscanf(fd, "%d ", &nbelefic);
        if (nbelefic < nbele) {
          nbele = nbelefic;
        }
        v.v = (int *) malloc (nbele* sizeof(int));
        assert(v.v);
        for(i=0; i < nbele; i++) {
          fscanf(fd, "%d", &tmp);
          v.v[i] = tmp;
        }
        return v;
      }
      
      Write such a file should not worry you :
      void ecrire (FILE * fd, vect_t w) {
        int i;
        fprintf(fd,"%9d ",w.nbele);
        printf(" on met %d\n",w.nbele);
        for (i = 0; i < w.nbele; i++)
          fprintf(fd,"%9d ",w.v[i]);
        fprintf(fd,"\n");
      }
      
      All that remain are two simple functions.
      vect_t creervect(nb) {
        int i, a, b, c;
        vect_t w;
        w.nbele = nb;
        w.v = (int *) malloc (nb * sizeof(int));
        assert(w.v);
        a = 1;
        b = 1;
        w.v[0]=1;
        w.v[1]=1;
        for (i = 2; i < nb; i++) {
          c = a + b;
          w.v[i] = c;
          b = a;
          a = c;
        }
        return w;
      }
      
      int main (int argc, char * * argv) {
        int nb, lireou;
        FILE * fd;
        vect_t w;
      
        if (argc != 3) {
          printf(" Mode : a.out nb nmfic\rn nb est un entier et nmfic un nom de fichier\n");
          exit (0);
        }
        nb = atoi (argv[1]);
        printf("argv[1] valait %d\n", nb);
        printf("Pour lire (1) ou ecrire (2) ?\n");
        scanf("%d",&lireou);
        if (lireou == 1) {
          fd = fopen(argv[2],"r+");/* open the file to read it*/
          assert(fd);
          w = lire(fd,nb);
          afficv(w);
          printf("Ou suis-je ? %ld\n",ftell(fd));
          fseek(fd, 0L, 0);
          printf("Ou suis-je ? %ld\n",ftell(fd));
          fseek(fd, -10L, SEEK_END);
          printf("Ou suis-je ? %ld\n",ftell(fd));
          fclose(fd);
          exit (0);
        }
        printf("On va ecrire \n");
        fd = fopen(argv[2],"w");/* open the file to write it*/
        assert(fd);
        w = creervect(nb);
        afficv(w);
        ecrire(fd,w);
        fclose(fd);
        exit (0);
      
      }
      /*    r      r+     w      w+     a       a+
      Où    debut debut début  début  fin     fin
      non   err   err           creation
      
      ftell(fd) -> le numéro de la case du fichier (0 c'est le début, 1 le second octet...)
      fseed(fd,ecart,point) permet d'avancer de "ecart" octets à partir du "point"
      SEEK_SET  0
      SEEK_CUR  1
      SEEK_END  2
      */
      
    2. Second example
      #include <stdio.h>
      #include <stdlib.h>
      
      struct vectd {
        int nb;
        double * v;
      } ;
      typedef struct vectd vectd_t;
      
      Then we are able to write such a vector on the terminal:
      void afficher (vectd_t v){
        int i;
      
        printf("%d ",v.nb);
        for (i = 0; i < v.nb; i++)
      	printf("%lf ",v.v[i]);
        printf("\n");
      }
      
      ... or on the file:
      void ecrire (vectd_t v, char * nomfichier) {
        int i;
        FILE * fd;
      
        fd = fopen (nomfichier, "w");
        if (! fd) { /* good old ways */
      	printf("Impossible d ouvrir le fichier %s !\n\n", nomfichier);
      	exit (1);
        }
        fprintf(fd,"%d ",v.nb);
        for (i = 0; i < v.nb; i++)
      	fprintf(fd,"%lf ",v.v[i]);
        fclose(fd);
      }
      
      we need to create it:
      vectd_t creer (int nb) {
        vectd_t v;
        int i;
        double x, eps;
        
        v.nb = nb;
        v.v = (double *) malloc (nb * sizeof(double));
        if (! v.v) {
      	printf("Impossible d allouer la memoire de taille %d !\n\n", nb);
      	exit (1);
        }
        eps = - 0.07;
        for (i = 0, x=1.3; i < v.nb; i++) {
      	v.v[i] = x;
      	x += eps;
      	if (x < 1.0) 
      	  eps = 0.145 + eps;
      	if (x > 1.7) 
      	  eps = - 0.145 + eps;
        }
        return v;
      }
      
      or to read it from the file:
      vectd_t lire (char * nomfichier) {
        vectd_t v;
        int i, nb;
        FILE * fd;
        double x;
        
        fd = fopen (nomfichier, "r");
        if (! fd) {
      	printf("Impossible d ouvrir le fichier %s !\n\n", nomfichier);
      	exit (1);
        }
        fscanf(fd,"%d ",&nb);
        v.nb = nb;
        v.v = (double *) malloc (nb * sizeof(double));
        if (! v.v) {
      	printf("Impossible d allouer la memoire de taille %d !\n\n", nb);
      	exit (1);
        }
        for (i = 0; i < v.nb; i++) {
      	fscanf(fd,"%lf ",&x);
      	v.v[i] = x;
        }
        fclose(fd);
        return v;
      }
      
      Here is a main function to use all the functions above.
      
      int main (int argc, char * * argv) {
        int nb, choix;
        vectd_t w;
        char * nf = "donnees.txt";
        
        choix = 0;
        if (argc == 3) {
      	choix = atoi (argv[1]);
      	nb = atoi (argv[2]);
      	printf("creation du fichier avec %d valeurs\n", nb);
      	w = creer(nb);
      	afficher(w);
      	ecrire(w,nf);
      	return 0;
        }
        if (argc == 2) {
      	choix = atoi (argv[1]);
        }
        if (choix < 0 || choix > 1)
      	choix = 0;
        switch(choix) {
        case 0:
      	printf("creation du fichier\n");
      	nb = 21;
      	w = creer(nb);
      	afficher(w);
      	ecrire(w,nf);
      	break;
        case 1:
      	printf("lecture du fichier\n");
      	w = lire(nf);
      	afficher(w);
      	break;
        }
      }
      
    3. Second example

      We need to read and write files. Let's start with some definitions.

      Nous allons voir maintenant comment on écrit et comment on peut lire les contenus de fichiers. D'abord, il nous faut quelques définitions, c'est le fichier mydef.h :
    4. Third example

    5. A quicker version

      Version plus rapide

      The prototypes and definitions are given at first:
      Nous donnons ici simplement les fichiers direct.h, qui contient les décalrations et prototypes,
      /* direct.h*/
      #include <stdio.h>
      #include <stdlib.h>
      
      int fibiter (int);
      int lire (FILE *, int);
      int remplir (FILE *, int);
      

      the file with the main function

      puis le fichier contenant la fonction main
      /* miam.c */
      #include "direct.h"
      #define NOM "fib.dat"
      #define NORMAL 43
      
      main (int argc, char * * argv) {
        FILE * fichier;
        int i, j, v, w;
      
        if (argc == 2) {
      	i = atoi (argv [1]);
      	if ((fichier = fopen(NOM,"r+"))!= NULL) {
      //	  printf("On cherche fib(%d)\n",i);
      	  v = lire (fichier, i);
      	  printf("fib(%d) = %d\n",i,v);
      	}
      	else {
      	  if ((fichier = fopen(NOM,"w+"))== NULL) {
      		printf(" Rien a faire nous revenons a une fonction classique\n");
      		v = fibiter (i);
      		printf("fib(%d) = %d\n",i,v);
      	  }
      	  else {
      		v = remplir (fichier, i);
      		printf("fib(%d) = %d \n",i,v);
      	  }
      	}
      	fclose(fichier);
        }
        else { /* Taille indefinie  */
      	if ((fichier = fopen(NOM,"r+"))== NULL) {
      	  printf("il faut creer ce fichier \n\n");
      	  fichier = fopen(NOM,"w+");
      	  v = remplir (fichier, NORMAL);
      	  w = lire (fichier, NORMAL);
      	  printf("fib(%d) = %d == %d\n",NORMAL,v, w);
      	  fclose (fichier);
      	}
      	else {
      	  v = lire (fichier, NORMAL);
      	  printf("fib(%d) = %d \n",NORMAL,v);
      	  close(fichier);
      	}
        }
      }
      

      then the two functions of file direct.c

      puis les deux fonctions qui composent le fichier direct.c, lire
      /*direct.c*/
      /* Il s agit de lire et d ecrire dans un fichier massif */
      #include "direct.h"
      
      int lire (FILE * f, int nb) {
        int nf, i, v, w, * tf;
      
      /* on commence par savoir s'il y a assez d'elements dans f*/
      
        fread(&nf, (size_t) 4, (size_t) 1, f);
        printf("Dans ce fichier il y a %d elements\n",nf);
        if (nf > nb) {
      	tf = (int *) malloc ((nb+1) * sizeof (int));
      	fread(tf, (size_t) 4, (size_t) (1 + nb), f);
      	for (i=0; i <= nb; i++) {
      	  printf("fib(%d) == %d\n",i, tf[i]);
      	}
      	return tf[nb];
        }
        else {
      	return remplir (f, nb);
        }
      }
      
      then remplir(fill it)
      puis remplir
      int remplir (FILE * f, int nb) {
        int nf, i, v, w, * tf;
      
        tf = (int *) malloc ((nb+1) * sizeof (int));
        tf[0] = 1;
        tf[1] = 1;
        for (i=2, v=1, w=1; i <= nb; i++) {
      	v += w;
      	tf[i] = v;
      	w = v - w;
        }
        for (i=0; i < nb; i++) {
      	printf("fib(%d) = %d\n",i,tf[i]);
        }
        fseek (f,0,SEEK_SET);
        fwrite(&nb, (size_t) 4, (size_t) (1), f);
        fwrite(tf, (size_t) 4, (size_t) (1 + nb), f);
        return tf[nb];
      }
      

      All that is to do now is compile and run the program.
      Il ne reste plus qu'à copiler.

          V.A) Projets : un exemple de rapport

      Vous pouvez utiliser LaTeX pour rédiger votre projet. Pour cela vous trouverez ici plusieurs fichiers :
      Dernière mise à jour le 31/3/2022 14h00