Site de Jean-Michel RICHER

Maître de Conférences en Informatique à l'Université d'Angers

Ce site est en cours de reconstruction certains liens peuvent ne pas fonctionner ou certaines images peuvent ne pas s'afficher.


stacks

1. Introduction au C++

1.2. Le langage C

Cette partie est une brève introduction au langage.

1.2.1. Types de données

Il existe en langage C des types dits scalaires (de base) :

  • char : charactère (octet)
  • int : entier (32 ou 64 bits)
  • float : réel 32 bits format IEEE 754
  • double : réel 64 bits format IEEE 754

On peut également combiner des préfixes :

  • unsigned pour spécifier une donnée non signée (entier naturel)
  • short ou long pour modifier le nombres d'octets utilisés pour le codage

Voici quelques exemples de combinaisons :

  • unsigned char
  • short int (16 bits)
  • long int
  • unsigned int
  • unsigned short int
  • unsigned long int
  • long long int
  • long double

L'opérateur sizeof permet de connaître la taille en octets occupée par un type.

 type   taille
(octets) 
 signed
min 
  signed
max 
 unsigned
min 
 unsigned
max 
 char   1   -128   +127   0   255 
 short int   2   -32768   32767   0   65535 
 int   4   -2147483648   +2147483647   0   4294967295 
 long int   4   2147483648   +2147483647   0   4294967295 
 long lont int   8   -9 223 372 036 854 775 807   +9 223 372 036 854 775 807   0   +18 446 744 073 709 551 615 
Types entiers en langage C en architecture 32 bits
 type   taille
(octets) 
 min    max   décimales 
 float   4   1.2E-38   3.4E+38   6 
 double   8   2.3E-308   1.7E+308   15 
 long double   12 ou 16   3.4E-4932   1.1E+4932   19 
Types flottants en langage C
Afficher le code    ens/inra/c1_langage_types.cpp
  1. #include <iostream>
  2. using namespace std;
  3.  
  4. int main() {
  5.   cout << "sizeof(char) = " << sizeof(char) << endl;
  6.   cout << "sizeof(short int) = " << sizeof(short int) << endl;
  7.   cout << "sizeof(int) = " << sizeof(int) << endl;
  8.   cout << "sizeof(long int) = " << sizeof(long int) << endl;
  9.   cout << "sizeof(long long int) = " << sizeof(long long int) << endl;
  10.   cout << "sizeof(float) = " << sizeof(float) << endl;
  11.   cout << "sizeof(double) = " << sizeof(double) << endl;
  12.   cout << "sizeof(long double) = " << sizeof(long double) << endl;
  13.   return 0;
  14. }
  15.  
****************************
*** architecture 32 bits ***
****************************
sizeof(char) = 1
sizeof(short int) = 2
sizeof(int) = 4
sizeof(long int) = 4
sizeof(long long int) = 8
sizeof(float) = 4
sizeof(double) = 8
sizeof(long double) = 12
****************************
*** architecture 64 bits ***
****************************
sizeof(char) = 1
sizeof(short int) = 2
sizeof(int) = 4
sizeof(long int) = 8
sizeof(long long int) = 8
sizeof(float) = 4
sizeof(double) = 8
sizeof(long double) = 16

1.2.2. Types et types structurés

On utilise le mot clé struct combiné à typedef afin de définir de nouveau types :

Afficher le code    ens/inra/c1_struct_typedef.cpp
  1. typedef short int int16_t;
  2. typedef unsigned short int uint16_t;
  3.  
  4. /**
  5.  * première forme pour définir une personne
  6.  */
  7. struct _Person1 {
  8.   int age;
  9.   char name[25];
  10. };
  11.  
  12. typedef struct _Person1 Person1;
  13.  
  14. /**
  15.  * deuxième forme pour définir une personne (condensé)
  16.  */
  17. typedef struct {
  18.   int age;
  19.   char name[25];
  20. } Person2;
  21.  
  22. /**
  23.  * structure auto-référencée pour liste chaînée
  24.  */
  25. struct _Link {
  26.   struct _Link *prev, *next;
  27.   int value;
  28. };
  29.  
  30. typedef struct _Link Link;
  31.  

1.2.3. Déclaration des variables

La déclaration de variable se fait en commençant par définir le type suivi de l'identificateur.

Afficher le code    ens/inra/c1_decl_variable.cpp
  1. // déclaration de x de type entier, non initialisée
  2. int x;
  3.  
  4. // déclaration de y de type entier, initialisée à 0
  5. int y = 0;
  6.  
  7. // plusieurs déclarations
  8. int z, t = 1, u = 2, k;
  9.  
  10. // attention cependant pour les pointeurs
  11. // ici x est un pointeur sur un entier
  12. // y et z sont des entiers
  13. int *x, y, z;
  14.  
  15. // par contre ici x, y ,z sont des pointeurs sur un entier
  16. int *x, *y, *z;
  17.  

1.2.4. Les tableaux

On peut déclarer les tableaux de deux manières différentes :

  • statique : la taille est connue, le tableau est défini dans le segment de données du programme (si variable globale), ou la pile (si variable locale ! Attention !)
  • dynamique : la taille n'est pas forcément connue à la compilation, le tableau est alloué dans le tas (heap), il faut réaliser l'allocation mémoire du tableau

Attention, pour un tableau composé de N éléments :

  • le premier indice est toujours 0
  • le dernier indice est toujours N-1
Afficher le code    ens/inra/c1_static_array.cpp
  1. // tableau de 20 entiers
  2. int tab_int[20];
  3.  
  4. // initialisation
  5. for (int i=0; i<20; ++i) {
  6.   tab_int[i] = 0;
  7. }
  8.  
  9. // tableau de 100 Person
  10. Person tab_person[100];
  11.  
  12.  

Note : lorsque l'on déclare une variable ou un tableau localement (dans une fonction) celui-ci est alloué dans la pile. Sous Unix/Linux par exemple la taille de la pile est par défaut de 8192 ko (soit 8 Mo) (cf commande shell ulimit -a).

Si on déclare un tableau trop grand on risque de saturer la pile

Afficher le code    ens/inra/c1_stack_overflow.cpp
  1. #include <iostream>
  2. using namespace std;
  3.  
  4. int main() {
  5.   // note: 2097152 * 4 = 8 * 1024 * 1024
  6.   int tab[2097153];
  7.  
  8.   cout << "hello world !" << endl;
  9.   return 0;
  10. }
  11.  
./c1_segmentation_fault.exe
Segmentation fault (core dumped)

Les chaînes de caractères

Il n'y a pas de type chaîne de caractères en langage C, une chaîne de caractères est représentée par un tableau de caractères. La fin de la chaine est représentée par le caractère '\0'.

Les chaines sont introduites pas le symbole double guillemet : "abc"

Les caractères sont introduits pas le symbole simple guillemet : 'a'

Une chaine de longueur 3 caractères comme "abc" occupe donc 4 caractères : 'a', 'b', 'c', '\0'.

Plusieurs fonctions issues de la librairire string.h permettent de manipuler les tableaux de caractères :

  • size_t strlen(const char *s); : donne la longueur d'une chaine
  • char *strcpy(char *dest, const char *src); : copie une chaine source dans une chaine destination
  • char *strncpy(char *dest, const char *src, size_t n); idem mais au maximum n caractères
  • char *strcat(char *dest, const char *src); : concaténation
  • char *strncat(char *dest, const char *src, size_t n); : concaténation avec au maximum n caractères

Les caractères spéciaux sont préfixés par un caractère \ :

  • \n : new line
  • \r : retour chariot
  • \t : tabulation horizontale
Afficher le code    ens/inra/c1_string.cpp
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4.  
  5. int main(int argc, char *argv[]) {
  6.  
  7.   const char *str1 = "abrac";
  8.   char str2[] = "ada";
  9.  
  10.   char str[30];
  11.  
  12.   strcpy(str, str1);
  13.   strcat(str, str2);
  14.   strncat(str, &str[1], 3);
  15.  
  16.   printf("final string = %s of length %lu\n", str, strlen(str));
  17.  
  18.   int x = 1; int y = 2;
  19.  
  20.   int z = x++ + ++y;
  21.   printf("z = %d\n", z);
  22.   printf("x = %d\n", x);
  23.   printf("y = %d\n", y);
  24.   return 0;
  25. }
  26.  
final string = abracadabra of length 11

1.2.5. Les opérateurs

Attention, certain opérateurs possèdent plusieurs significations, notamment &, |, *.

 Symbole   Signification 
 [ ]   accès élément d'un tableau 
 !   négation logique (NOT) 
 ~   complément à 1 (NOT) 
 + - / *   opérateurs arithmétiques classiques 
 *   accès valeur d'un pointeur 
 ++   pré ou post incrémentation 
 --   pré ou post décrémentation 
 &   et binaire / adresse d'une variable 
 |   ou binaire 
 &&   et logique (AND) 
 ||   ou logique (OR) 
 < >   inférieur, supérieur 
 <= >=   inférieur ou égal, supérieur ou égal 
 =   opérateur d'affectation 
 ==   test d'égalité 
 !=   test de différence 
 .   accès à un attribut d'une structure 
 ->   accès à un attribut d'une structure par pointeur 
Opérateurs en langage C

1.2.5.a  post et pré incrémentation et décrémentation

Les opérateurs de post et pré incrémentation (ou décrémentation) combinés à l'opérateur d'affectation possèdent un comportement différent en fonction qu'ils sont positionnés avant ou après la valeur à modifier.

  • en pré-incrémentation, l'opérateur ++ commence par incrémenter la variable située à sa droite, puis retourne la nouvelle valeur
  • en post-incrémentation, l'opérateur ++ retourne la valeur actuelle de la variable puis l'incrémente
Afficher le code    ens/inra/c1_pre_post_incr.cpp
  1. int a = 1;
  2. int b = 1;
  3. int c, d;
  4.  
  5. c = ++a; // like a = a + 1; c = a;
  6. d = b++; // like d = b; b = b + 1;
  7.  
  8. // finally
  9. // a = 2, b = 2, c = 2, d = 1
  10.  

Comment interpréter alors l'expression suivante : z = x +++ y ?

int x = 1; 
int y = 2;

int z = x+++y; // x++ + y donc z = 3, x = 2, y = 2

1.2.5.b  l'opérateur () : ?

Il permet de réaliser un if then else sous forme condensée :

Afficher le code    ens/inra/c1_condensed_if_then_else.cpp
  1. int a = 1;
  2. int b = 2.
  3. int x;
  4.  
  5. if (a < b) {
  6.   x = 0
  7. } else {
  8.   x = 1;
  9. }
  10.  
  11. // ou en condensé
  12. x = (a < b) ? 0 : 1;
  13.  

1.2.6. Les structures de contrôle

1.2.6.a  for

for (initialisations; condition; incrémentations) { 
	instructions;
}
Afficher le code    ens/inra/c1_control_for.cpp
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3.  
  4. int main() {
  5.   int i, sum, tab[100];
  6.  
  7.   for (i=0; i<100; ++i) {
  8.     tab[i] = i+1;
  9.   }
  10.  
  11.   for (i=0, sum = 0; (i<100) && (sum < 50); ++i) {
  12.     sum += tab[i];
  13.   }
  14.  
  15.   printf("sum = %d\n", sum);
  16.  
  17.   return 0;
  18. }
  19.  

Dans une boucle for / while on peut utiliser deux instructions particulières :

  • break : permet de sortir de la boucle
  • continue : permet de sauter les instructions qui suivent
Afficher le code    ens/inra/c1_control_for_break_continue.cpp
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3.  
  4. int main() {
  5.   int i, sum = 0;
  6.  
  7.   // on calcule la somme des i impairs
  8.   for (i=0; i<100; ++i) {
  9.     // on sort de la boucle lorsque i == 50
  10.     if (i == 50) {
  11.       break;
  12.     }
  13.     // si i est pair alors on passe à i+1
  14.     if ((i % 2) == 0) {
  15.       continue;
  16.     }
  17.    
  18.     sum += i;
  19.   }
  20.  
  21.   printf("sum = %d\n", sum);
  22.  
  23.   return 0;
  24. }
  25.  

1.2.6.b  if then else

if (condition) {
	instructions-then;
} else {
	instructions-else;
}

if (condition) 
	une-instruction-then;
else 
	une-instruction-else;
Afficher le code    ens/inra/c1_control_if.cpp
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3.  
  4. int main() {
  5.   int i;
  6.  
  7.   srand(19702013);
  8.  
  9.   for (i = 1; i < 100; ++i) {
  10.     if ((i % 2) == 0) {
  11.       printf("%d is even\n", i);
  12.     }
  13.   }
  14.  
  15.   for (i = 1; i < 100; ++i) {
  16.     int x = rand() % 10;
  17.     if (x < 5) {
  18.       printf("%d is lower than 5\n", x);
  19.     } else {
  20.       printf("%d is greater or equal to 5\n", x);
  21.     }
  22.   }
  23.  
  24.   for (i = 1; i < 100; ++i) {
  25.     int x = rand() % 10;
  26.     if (x < 5) {
  27.       printf("%d is lower than 5\n", x);
  28.     } else if (x <= 7) {
  29.       printf("%d is in [5..7]\n", x);
  30.     } else {
  31.       printf("%d is greater than 7\n", x);
  32.     }
  33.   }
  34.  
  35.  
  36. }
  37.  
  38.  

1.2.6.c  while

while (condition) {
	instructions;
}

while (condition) une-instruction;
Afficher le code    ens/inra/c1_control_while.cpp
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3.  
  4. int main() {
  5.   int i, sum, tab[100];
  6.  
  7.   i=0;
  8.   while (i<100) {
  9.     tab[i] = i+1;
  10.     ++i;
  11.   }
  12.  
  13.   i=0;
  14.   sum = 0;
  15.   while ((i<100) && (sum < 50)) {
  16.     sum += tab[i];
  17.     ++i;
  18.   }
  19.  
  20.   printf("sum = %d\n", sum);
  21.  
  22.   return 0;
  23. }
  24.  

1.2.6.d  repeat until = do while

do {
	instructions;
} while (condition);
Afficher le code    ens/inra/c1_control_do_while.cpp
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3.  
  4. int main() {
  5.   int i, sum, tab[100];
  6.  
  7.   i=0;
  8.   while (i<100) {
  9.     tab[i] = i+1;
  10.     ++i;
  11.   }
  12.  
  13.   i=0;
  14.   sum = 0;
  15.   do {
  16.     sum += tab[i];
  17.     ++i; 
  18.   } while ((i<100) && (sum < 50));
  19.  
  20.   printf("sum = %d\n", sum);
  21.  
  22.   return 0;
  23. }
  24.  

1.2.6.e  case of = switch / case

switch (valeur-entière) {
	case VALUE_1: instructions; break;
	case VALUE_2: instructions; break;
	...
	default : instructions; break;
}
Afficher le code    ens/inra/c1_control_switch.cpp
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3.  
  4. int main() {
  5.   int i, sum, tab[100], count[5];
  6.  
  7.   srand(19702013);
  8.  
  9.   for (i=0; i<100; ++i) {
  10.     tab[i] = rand() % 5;
  11.   }
  12.  
  13.   for (i=0; i<5; ++i) {
  14.     count[i] = 0;
  15.   }
  16.  
  17.   sum = 0;
  18.   for (i=0; i<100; ++i) {
  19.     int x = tab[i];
  20.     switch(x) {
  21.       case 0: ++count[x]; break;
  22.       case 1:
  23.       case 2: sum += x; break;
  24.       default: printf("%d not taken into account\n", x);
  25.     }
  26.   }
  27.  
  28.   printf("sum = %d\n", sum);
  29.  
  30.   return 0;
  31. }
  32.  

1.2.7. Les pointeurs et l'allocation mémoire

Un pointeur est une variable qui contient une adresse mémoire

En C, un pointeur possède deux fonctionnalités :

  • il permet de faire référence à une variable de même type.
  • il permet de créer de nouvelles variables / tableaux

Trois opérateurs sont utilisés avec les pointeurs :

  • * qui permet à la fois
    • de déclarer le pointeur
    • ou de faire référence à la valeur sur laquelle il pointe
  • & qui donne l'adresse d'une variable
  • -> qui donne accès à un attribut d'une structure de données

Enfin la constante NULL permet d'identifier un pointeur vide, elle est renommée en nullptr en C++ 11.

1.2.7.a  Exemple

Dans l'exemple qui suit:

pointeur

  • p est un pointeur sur un entier et occupe 4 octets = sizeof(int *), il est situé à l'adresse 64 en mémoire.
  • la variable entière x est située à l'adresse mémoire 32 et contient la valeur -1 codée sur 4 octets = sizeof(int)
  • du fait de la dualité pointeur / tableau, p peut être vu comme un tableau tel que:
    • p[0] à pour adresse celle de la variable x, c'est à dire 32
    • p[1] à pour adresse les 4 octets consécutifs à x, soit 36
    • p[i] à pour adresse 32 + i * sizeof(int)
    • en particulier p[8] à pour adresse celle du pointeur p, soit 64

Le symbole * a trois sémantiques différentes :

  • dans la déclaration int *p; il signifie pointeur sur un entier
  • dans l'affectation *p = *p + 1;
    • situé à droite du symbole = (right-value, valeur en lecture), il signifie la valeur à l'adresse p
    • situé à gauche su symbole = (left-value, valeur en écriture), il signifie affecter à l'adresse contenue dans p

ce qui, d'un point de vue assembleur, se traduit par exemple par :

; on traduit :
;
; *p = *p + 1
;
; === p est représenté par le registre ebx ===
;
mov  eax, [ebx]  ; r-value en lecture (load)
add  eax, 1      ; ajouter 1 à *p
mov  [ebx], eax  ; l-value en écriture (store)

1.2.7.b  Manipulation de données existantes par l'intermédiaire d'un pointeur

Afficher le code    ens/inra/c1_pointer.cpp
  1. #include <stdlib.h>
  2. #include <stdio.h>
  3.  
  4.  
  5. int main() {
  6.  
  7.   // déclaration d'une variable entière
  8.   int x = 1;
  9.  
  10.   // déclaration d'un pointeur
  11.   int *p;
  12.  
  13.   // affectation du pointeur pour être positionné sur x
  14.   p = &x;
  15.  
  16.   printf("x = %d, *p = %d\n", x, *p);
  17.  
  18.   // modification de la valeur de x par l'intermédiaire du pointeur
  19.   *p = 2;
  20.  
  21.   // lecture de la valeur de x par l'intermédiaire du pointeur
  22.   int y = *p + 1; // y = 2 + 1 = 3
  23.  
  24.   printf("y = %d, *p = %d\n", y, *p);
  25.  
  26.   // déclaration de q qui pointe sur x puisqu'on lui affecte
  27.   // l'adresse de p
  28.   int *q = p;
  29.  
  30.   printf("*q = %d\n", *q);
  31.  
  32.   return 0;
  33. }
  34.  

1.2.7.c  Création de nouvelles données

Il existe plusieurs fonctions pour allouer et libérer la mémoire :

  • void *malloc(size_t size) : alloue size octets dans le tas (heap)
  • void *calloc(size_t nmemb, size_t size) : alloue nmemb*size octets
  • free(void *ptr) : libère la mémoire allouée
  • void *_mm_malloc(size_t size, size_t align) : allocation avec alignement de l'adresse sur une valeur multiple de align (choisir 16 ou 32)
  • void _mm_free(void *ptr) : libère la mémoire allouée par mm_malloc


Afficher le code    ens/inra/c1_dynamic_variable.cpp
  1. #include <stdlib.h>
  2. #include <stdio.h>
  3.  
  4. // ----------------------------------------------
  5. // main function
  6. // ----------------------------------------------
  7. int main() {
  8.   // déclaration d'un pointeur sur un entier
  9.   int *x;
  10.  
  11.   // création de l'espace de stockage nécessaire
  12.   x = (int *) malloc(sizeof(int));
  13.  
  14.   // initialisation de l'entier pointé par x à la valeur 1
  15.   *x = 1;
  16.  
  17.   printf("x = %p, *x = %d\n", x, *x);
  18.  
  19.   // libération de l'espace alloué
  20.   free(x);
  21.  
  22.   return 0;
  23. }
  24.  
Afficher le code    ens/inra/c1_dynamic_array.cpp
  1. // compiler avec -msse2 car on utilise xmmintrin.h
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4. #include <xmmintrin.h>
  5.  
  6. /**
  7.  * classe contenant un entier utilisée pour cet exemple
  8.  */
  9. class MyClass {
  10. protected:
  11.   int value;
  12. public:
  13.   /**
  14.    * constructeur par défaut sans argument
  15.    */
  16.   MyClass() {
  17.     value = 0;
  18.   }
  19.  
  20.   /**
  21.    * constructeur avec valeur d'initialisation
  22.    */
  23.   MyClass(int v) {
  24.     value = v;
  25.   }
  26.  
  27.   /**
  28.    * getter
  29.    */
  30.   int get_value() {
  31.     return value;
  32.   }
  33. };
  34.  
  35. // ----------------------------------------------
  36. // main function
  37. // ----------------------------------------------
  38. int main() {
  39.  
  40.   // allocation d'un tableau de 20 entiers
  41.   int *tab_int;
  42.  
  43.   tab_int = (int *) malloc(20 * sizeof(int));
  44.   // ou
  45.   tab_int = static_cast<int *>(malloc(20 * sizeof(int)));
  46.   // ou
  47.   tab_int = (int *) calloc(20, sizeof(int));
  48.  
  49.   // libération de la mémoire allouée
  50.   free(tab_int);
  51.  
  52.  
  53.   // ==================================
  54.   // allocation avec alignement
  55.   // ==================================
  56.  
  57.   tab_int = (int *) _mm_malloc(20 * sizeof(int), 16);
  58.  
  59.   // libération de la mémoire allouée par _mm_malloc
  60.   _mm_free(tab_int);
  61.  
  62.  
  63.   // tableau de 100 Person
  64.   MyClass *tab_object;
  65.  
  66.   tab_object = (MyClass *) malloc(100 * sizeof(MyClass));
  67.   // ou
  68.   tab_object = static_cast<MyClass *>(malloc(100 * sizeof(MyClass)));
  69.   // ou, mais nécessite de déclarer le constructeur par défaut sans arguments
  70.   tab_object = new MyClass[ 100 ];
  71.  
  72.   for (int i=0; i<100; ++i) {
  73.     printf("%d ", tab_object[i].get_value() );
  74.   }
  75.   // libération
  76.   free(tab_object);
  77.    
  78.   return 0;
  79. }
  80.  
Afficher le code    ens/inra/c1_dynamic_struct.cpp
  1. struct _Link {
  2.   struct _Link *prev, *next;
  3.   int value;
  4. };
  5.  
  6. typedef struct _Link Link;
  7.  
  8. Link a = (Link *) malloc(sizeof(Link));
  9. a->prev = a->next = NULL;
  10. a->value = 1;
  11.  
  12.  
  13.  

1.2.8. Entrées et sorties

Pour les entrées et sorties standard on utilise les flux suivants :

  • stdout : affichage standard écran/console, bufferisé
  • stderr : affichade des erreurs écran/console, non bufferisé
  • stdin : entrée standard, clavier

Pour la lecture on utilise scanf, fscanf, pour l'écriture printf ou fprintf :

Afficher le code    ens/inra/c1_printf_scanf.cpp
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4.  
  5. // ----------------------------------------------
  6. // main function
  7. // ----------------------------------------------
  8. int main() {
  9.   int x, y;
  10.   // str is NULL so it will be allocated by getline
  11.   char *str = NULL;
  12.   size_t size;
  13.  
  14.   // will read two integers followed by '\n'
  15.   fscanf(stdin, "%d%d\n", &x, &y);
  16.  
  17.   printf("x = %d, y = %d\n", x, y);
  18.  
  19.   fflush(stdin);
  20.   getline(&str, &size, stdin);
  21.  
  22.   fprintf(stderr, "str = [%s] of allocated size = %lu and real size %lu\n", str, size, strlen(str));
  23.   free(str);
  24.  
  25.   return 0;
  26. }
  27.  

Lorsqu'on désire ouvrir ou lire un fichier il faut utiliser un descripteur de fichier FILE *, ainsi que les fonctions :

  • FILE *fopen(const char *path, const char *mode);
  • int fclose(FILE *fp);
  • pour les fichiers textes on utilise :
    • int fscanf(FILE *stream, const char *format, ...)
    • int fprintf(FILE *stream, const char *format, ...)
  • pour les fichier binaires, on utilise :
    • size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
    • size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
Afficher le code    ens/inra/c1_printf_scanf_file.cpp
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4.  
  5. // ----------------------------------------------
  6. // main function
  7. // ----------------------------------------------
  8. int main() {
  9.   // input file
  10.   FILE *in;
  11.   // output file
  12.   FILE *out;
  13.  
  14.   int i, value;
  15.   const char *file_name = "data.txt";
  16.  
  17.   srand(19702013);
  18.  
  19.   // ------------------------------------------
  20.   // generate file
  21.   // ------------------------------------------
  22.   out = fopen(file_name, "w");
  23.   if (!out) {
  24.     fprintf(stderr, "error: could not open file for writing: %s", file_name);
  25.     exit(EXIT_FAILURE);
  26.   }
  27.  
  28.   for (i = 0; i<10; ++i) {
  29.     fprintf(out, "%d\n", rand() % 100);
  30.   }
  31.  
  32.   for (i = 0; i<5; ++i) {
  33.     int j, length = (rand() % 10) + 2;
  34.     for (j = 0; j<length; ++j) {
  35.       fprintf(out, "%c", (char) (rand() % 26) + 65);
  36.     }
  37.     fprintf(out, "\n");
  38.   }
  39.   fclose(out);
  40.  
  41.   // ------------------------------------------
  42.   // read file
  43.   // ------------------------------------------
  44.   in = fopen(file_name, "r");
  45.   if (!out) {
  46.     fprintf(stderr, "error: could not open file for reading: %s", file_name);
  47.     exit(EXIT_FAILURE);
  48.   }
  49.   for (i = 0; i<10; ++i) {
  50.     fscanf(in, "%d\n", &value);
  51.     printf("read %d\n", value);
  52.   }
  53.   for (i = 0; i<5; ++i) {
  54.     char *str = NULL;
  55.     size_t size;
  56.     getline(&str, &size, in);
  57.     printf("read %s", str);
  58.     free(str);
  59.   }
  60.   fclose(in);
  61.  
  62.   return 0;
  63. }
  64.  

1.2.9. Le préprocesseur et les macro-instructions

En langage C, on réalise la séparation entre l'interface et l'implémentation :

  • l'interface est placée dans un fichier entête (header) d'extension .h et contient les définitions de constantes, de types, le prototype des sous-programmes
  • l'implémentation est placée dans une fichier source d'extension .c qui contient le code des sous-programmes

1.2.9.a  Le fichier header

Il commence toujours par la définition d'un littéral qui permet d'éviter de le charger plusieurs fois ce qui conduirait à générer des erreurs par le compilateur:

#ifndef STACK_H
#define STACK_H
...
#endif

includes

Afficher le code    ens/inra/c1_stack.h
  1. #ifndef STACK_H
  2. #define STACK_H
  3.  
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6.  
  7. #define MAX_ELEMENTS 100
  8.  
  9. typedef struct {
  10.   int index;
  11.   int *elements
  12. } Stack;
  13.  
  14. // initialize data structure
  15. void stack_create(Stack *s);
  16.  
  17. // remove data allocated by stack_create
  18. void stack_delete(Stack *s);
  19.  
  20. // push
  21. void stack_push(Stack *s, int v);
  22.  
  23. // pop
  24. void stack_pop(Stack *s);
  25.  
  26. // return element on top
  27. int stack_top(Stack *s);
  28.  
  29. // return 1 if stack is empty, 0 otherwise
  30. int stack_is_empty(Stack *s);
  31.  
  32. #endif
  33.  
  34.  
Afficher le code    ens/inra/c1_stack.c
  1. #include "c1_stack.h"
  2.  
  3. // initialize data structure
  4. void stack_create(Stack *s) {
  5.   s->index = -1;
  6.   s->elements = (int *) malloc(MAX_ELEMENTS * sizeof(int));
  7. }
  8.  
  9. // remove data allocated by stack_create
  10. void stack_delete(Stack *s) {
  11.   free(s->elements);
  12. }
  13.  
  14. // push
  15. void stack_push(Stack *s, int v) {
  16.   ++s->index;
  17.   if (s->index == MAX_ELEMENTS) {
  18.     printf("stack is full\n");
  19.     exit(1);
  20.   }
  21.   s->elements[s->index] = v;
  22. }
  23.  
  24. // pop
  25. void stack_pop(Stack *s) {
  26.   if (s->index == -1) {
  27.     printf("stack is empty\n");
  28.     exit(1);
  29.   }
  30.   --s->index;
  31. }
  32.  
  33. // return element on top
  34. int stack_top(Stack *s) {
  35.   if (s->index == -1) {
  36.     printf("stack is empty\n");
  37.     exit(1);
  38.   }
  39.   return s->elements[s->index];
  40. }
  41.  
  42. // return 1 if stack is empty, 0 otherwise
  43. int stack_is_empty(Stack *s) {
  44.   return (s->index == -1) ? 1 : 0;
  45. }
  46.  
  47. // main
  48. int main() {
  49.   int i, sum = 0;
  50.   Stack s;
  51.  
  52.   stack_create(&s);
  53.   for (i = 1; i<=10; ++i) {
  54.     stack_push(&s, i);
  55.   }
  56.  
  57.   while  (!stack_is_empty(&s)) {
  58.     sum += stack_top(&s);
  59.     stack_pop(&s);
  60.   }
  61.  
  62.   printf("sum = %d\n", sum);
  63.  
  64.   stack_delete(&s);
  65.   return 0;
  66. }
  67.  

1.2.9.b  Définition de constantes

Les constantes sont déclarées grâce au mot clé #define identifier code :

#define MAX_ELEMENTS 100
#define MAX_PLUS_1 (MAX_ELEMENTS + 1) 

1.2.9.c  Définition de macro-instructions

Les macro-instructions sont déclarées grâce au mot clé #define identifier(parameters) code et permettent de réutiliser du code :

#define SQUARE(X) (X)*(X)
int x = 2;
int y = SQUARE(x); 
Afficher le code    ens/inra/c1_macros.c
  1. #define SQUARE(X) (X)*(X)
  2. #define JOIN(X,Y) X##Y
  3. #define JOIN_STRING(X,Y) X#Y
  4.  
  5. #include <stdio.h>
  6.  
  7. int main() {
  8.   int x1 = 2, y1;
  9.   float x2 = 3.14, y2;
  10.   const char *str_x1 = JOIN_STRING("x",1);
  11.   const char *str_x2 = JOIN_STRING("x",2);
  12.  
  13.   y1 = SQUARE(x1);
  14.   y2 = SQUARE(x2);
  15.  
  16.   y2 = JOIN(x,1) + JOIN(x,2);
  17.  
  18.   printf("%s + %s  = %f\n", str_x1, str_x2, y2);
  19.   return 0;
  20. }
  21.  
x1 + x2 = 5.140000

1.2.10. Subtilités du langage C

Les règles d'associativité des opérateurs (gauche ou droite) permettent d'écrire de manière concise certains traitements, cependant l'interprétation est difficile pour le novice.

Afficher le code    ens/inra/c1_subtilite_c.cpp
  1. // different version of string copy
  2.  
  3. // ----------------------------------------------
  4. // version 1
  5. // ----------------------------------------------
  6. void copy_str(char *dst, char *src) {
  7.   int i = 0;
  8.   while (src[i] != '\0') {
  9.     dst[i] = src[i];
  10.     ++i;
  11.   }
  12.   // copy last character '\0'
  13.   dst[i] = src[i];
  14. }
  15.  
  16. // ----------------------------------------------
  17. // version 2
  18. // stop condition can be reduced to : src[i]
  19. // assignment dst[i] = src[i] has a value of src[i]
  20. // ----------------------------------------------
  21. void copy_str(char *dst, char *src) {
  22.   int i = 0;
  23.   // while ((dst[i] = src[i]) != '\0') ++i;
  24.   while (dst[i] = src[i]) ++i;
  25. }
  26.  
  27. // ----------------------------------------------
  28. // version 3
  29. // use of pointers and post incrementation
  30. // ----------------------------------------------
  31. void copy_str(char *dst, char *src) {
  32.   while (*dst++ = *src++) ;
  33. }
  34.  
  35.  
  36.  

1.2.11. Passage de paramètres aux sous-programmes : par valeur ou par adresse

Lors de l'appel d'un sous-programme on peut passer les paramètres de trois manières différentes :

  • par valeur : on crée une copie d'une valeur existante, on peut modifier la valeur à l'intérieur du sous-programme mais elle ne sera pas modifiée à l'extérieur
  • par adresse : on travaille directement sur la valeur, si on la modifie à l'intérieur du sous-programme elle sera modifiée à l'extérieur
  • par reference : introduit en C++, grosso modo, on peut dire que c'est un passage par adresse mais qui utilise la syntaxe d'un passage par valeur
Afficher le code    ens/inra/c1_passage_param.cpp
  1. #include <stdlib.h>
  2. #include <stdio.h>
  3.  
  4. int a = 1;
  5. int b = 2;
  6.  
  7.  
  8. /**
  9.  * compute (x+1) + (y+1)
  10.  */
  11. int sum_plus(int x, int *y) {
  12.   x = x + 1; // or ++x
  13.   *y = *y + 1; // or ++*y;
  14.    
  15.   return x + *y;
  16. }
  17.  
  18.  
  19. /**
  20.  * structure that is used to compute z = x @ y
  21.  * where @ = +, -, *, /, ...
  22.  */
  23. typedef struct {
  24.   int x, y, z;
  25. } Operation;
  26.  
  27. /**
  28.  * execute sum on operation but call by value
  29.  */
  30. void operation_plus_by_value(Operation op) {
  31.   op.z = op.x + op.y;
  32. }
  33.  
  34. /**
  35.  * execute sum on operation but call by address
  36.  */
  37. void operation_plus_by_address(Operation *op) {
  38.   op->z = op->x + op->y;
  39. }
  40.  
  41. /**
  42.  * execute sum on operation but call by reference
  43.  */
  44. void operation_plus_by_reference(Operation& op) {
  45.   // use the syntax of call by value
  46.   op.z = op.x + op.y;
  47. }
  48.  
  49.  
  50. /**
  51.  * main function
  52.  */
  53. int main() {
  54.   int c;
  55.  
  56.   printf("before sum_plus\n");
  57.   printf("a = %d\n", a);
  58.   printf("b = %d\n", b);
  59.  
  60.   c = sum_plus(a, &b);
  61.  
  62.   printf("-------------\n");
  63.   printf("after sum_plus\n");
  64.   printf("a = %d\n", a);
  65.   printf("b = %d\n", b);
  66.   printf("c = %d\n", c);
  67.  
  68.   // example with structure
  69.   Operation op1;
  70.   op1.x = 1; 
  71.   op1.y = 2;
  72.   op1.z = 0;
  73.   Operation op2 = op1;
  74.    
  75.   // call by value : KO, result won't be registered
  76.   operation_plus_by_value(op1);
  77.   printf("result of by value z = %d\n", op1.z);
  78.  
  79.   // call by address : OK
  80.   operation_plus_by_address(&op1);
  81.   printf("result of by address z = %d\n", op1.z);
  82.  
  83.   // call by reference : OK
  84.   operation_plus_by_reference(op2);
  85.   printf("result of by reference z = %d\n", op2.z);
  86.  
  87.   return 0;
  88. }
  89.  
  90.  
before sum_plus
a = 1
b = 2
-------------
after sum_plus
a = 1
b = 3
c = 5
result of by value z = 0
result of by address z = 3
result of by reference z = 3

paramètres par défaut

Notons qu'en C++, il est possible de passer des paramètres par défaut aux sous-programmes.

1.2.12. Arguments en ligne de commande d'un programme

On propose ici 3 méthodes différentes.

1.2.12.a  méthode argc, argv

Les arguments en ligne de commande peuvent être récupérés à partir de la fonction main qui est la fonction principale appelée après initialisation du programme.

Si aucun argument n'est fourni au programme alors :

  • argc = 1
  • argv[0] = nom du programme

Si on fournit $x$ arguments au programme alors :

  • argc = x + 1
  • argv[0] = nom du programme
  • argv[1] = premier argument
  • argv[x] = dernier argument
Afficher le code    ens/inra/c1_main_args.cpp
  1. #include <iostream>
  2. #include <cstdlib>
  3. using namespace std;
  4.  
  5. // ----------------------------------------------
  6. // main function
  7. // ----------------------------------------------
  8. int main(int argc, char *argv[]) {
  9.  
  10.   // check that program as at least three arguments
  11.   if (argc <= 3) {
  12.     cerr << "the program needs at least 3 arguments" << endl;
  13.     cerr << argv[0] << " method alpha message" << endl;
  14.     exit(EXIT_FAILURE);
  15.   }
  16.  
  17.   cout << "program's name = " << argv[0] << endl;
  18.   for (int i=1; i<argc; ++i) {
  19.     cout << "argument " << i << " = " << argv[i] << endl;
  20.   }
  21.  
  22.  
  23.   // first argument should be an integer
  24.   int method = 1;
  25.   if (argc > 1) {
  26.     // convert into integer
  27.     method = atoi(argv[1]);
  28.     cout << "method = " << method << endl;
  29.   }
  30.  
  31.   // second argument should be a float
  32.   float alpha = 0.2;
  33.   if (argc > 2) {
  34.     // convert into float
  35.     alpha = atof(argv[2]);
  36.     cout << "alpha = " << alpha << endl;
  37.   }
  38.  
  39.   // second argument should be a string
  40.   char *message = NULL;
  41.   if (argc > 3) {
  42.     message = argv[3];
  43.     cout << "message = " << message << endl;
  44.   }
  45.  
  46.  
  47.   return EXIT_SUCCESS; 
  48. }
  49.  
  50.  
> ./main_args.exe 1 2.15 "hello world" def ghij
program's name = ./main_args.exe
argument 1 = 1
argument 2 = 2.15
argument 3 = hello world
argument 4 = def
argument 5 = ghij
method = 1
alpha = 2.15
message = hello world

1.2.12.b  méthode getopt

Il est également possible d'utiliser une fonctionnalité de la librairie C : getopt

Afficher le code    ens/inra/c1_main_args_getopt.cpp
  1. #include <iostream>
  2. #include <cstdlib>
  3. using namespace std;
  4. #include <getopt.h>
  5.  
  6. // ----------------------------------------------
  7. // description of possible arguments
  8. // that can be used in
  9. // - short format : -m 1
  10. // - or long format : --method=1
  11. // ----------------------------------------------
  12. static struct option long_options[] = {
  13.   {"method",  required_argument, 0, 'm'},
  14.   {"alpha",   required_argument, 0, 'a'},
  15.   {"message", required_argument, 0, 's'},
  16.   {"verbose", no_argument,       0, 'v'},
  17.   {0,0,0,0}
  18.  
  19. };
  20.  
  21. // ----------------------------------------------
  22. // Definition of a structure that will gather the
  23. // parameters of the program
  24. // ----------------------------------------------
  25. typedef struct {
  26.   int method;
  27.   float alpha;
  28.   char *message;
  29.   bool verbose;
  30. } Parameters;
  31.  
  32. Parameters params;
  33.  
  34. // ----------------------------------------------
  35. // main function
  36. // ----------------------------------------------
  37. int main(int argc, char *argv[]) {
  38.  
  39.   params.verbose = false;
  40.  
  41.   if (argc <= 3) {
  42.     cerr << "the program needs at least 3 arguments" << endl;
  43.     cerr << argv[0] << " method alpha message" << endl;
  44.     exit(EXIT_FAILURE);
  45.   }
  46.  
  47.   cout << "program's name = " << argv[0] << endl;
  48.   for (int i=1; i<argc; ++i) {
  49.     cout << "argument " << i << " = " << argv[i] << endl;
  50.   }
  51.  
  52.   int option_index;
  53.   while (true) {
  54.     option_index = 0;
  55.     // the format m: means that the 'm' option needs one argument
  56.     // the option 'v' does not need argument so it is listed as 'v'
  57.         int c = getopt_long(argc, argv, "m:a:s:v", long_options, &option_index);
  58.         if (c == -1) break;
  59.  
  60.     switch(c) {
  61.       case 'm':
  62.         params.method = atoi(optarg);
  63.         break;
  64.       case 'a':
  65.         params.alpha = atof(optarg);
  66.         break;
  67.       case 's':
  68.         params.message = optarg; 
  69.         break;
  70.       case 'v':
  71.         params.verbose = true;
  72.         break; 
  73.     }
  74.   }
  75.  
  76.   if (params.verbose) {
  77.     cout << "method = " << params.method << endl;
  78.     cout << "alpha = " << params.alpha << endl;
  79.     cout << "message = " << params.message << endl;
  80.   }
  81.  
  82.   return EXIT_SUCCESS; 
  83. }
  84.  
  85.  
> ./main_args_getopt.exe --alpha=2.15 --method=1 --message="hello world" def ghij --verbose
program's name = ./main_args_getopt.exe
argument 1 = --alpha=2.15
argument 2 = --method=1
argument 3 = --message=hello world
argument 4 = def
argument 5 = ghij
argument 6 = --verbose
method = 1
alpha = 2.15
message = hello world

1.2.12.c  méthode CLAP (Command Line Argument Parser)

Voici enfin un système un peu plus complexe : cmdlarg.tgz et sa mise en application :

Afficher le code    ens/inra/temperature.cpp
  1. /**
  2.  * program that will convert temperatures from Celsius to Fahrenheit
  3.  * or from Fahrenheit to Celsius
  4.  */
  5. #include "command_line_argument_parser.h"
  6. #include <fstream>
  7. #include <cmath>
  8.  
  9. // --------------------------------------------------------
  10. // variables that the programmer must define
  11. // --------------------------------------------------------
  12. // name of program
  13. string program_name = "temperature.exe";
  14. // short description of what the program does
  15. string program_desc = "convert temperatures between celsius and fahrenheit";
  16.  
  17. // --------------------------------------------------------
  18. // variables related to command line arguments
  19. // --------------------------------------------------------
  20. // verbose level
  21. u32    verbose = 1;
  22.  
  23. float  initial_temperature = 0;
  24. float  final_temperature = 100.0;
  25. float  incr_temperature = 5.0;
  26.  
  27. // unit of initial to final temperatures
  28. u32    unit_id = 0;
  29. vector<string> unit_options = {
  30.   "celsius",
  31.   "fahrenheit"
  32. };
  33.  
  34. /**
  35.  * trigger function for command line argument used to
  36.  * check that temperatures are set correctly
  37.  */
  38. void trigger_temperature() {
  39.   if (initial_temperature >= final_temperature) {
  40.     ostringstream oss;
  41.     oss << "initial temperature (=" << initial_temperature << ") ";
  42.     oss << "should be inferior to final temperature (=" << final_temperature << ")";
  43.     throw std::logic_error(oss.str());
  44.   }
  45. }
  46.  
  47. /**
  48.  * convert from celsius to fahrenheit
  49.  */  
  50. void c2f() {
  51.   float temperature = initial_temperature;
  52.   while (temperature <= final_temperature) {
  53.     float f = 32.0 + (temperature * 5.0) / 9.0;
  54.     if (verbose > 0) { 
  55.       cout << temperature << "°C = " << f << "°F" << endl;
  56.     }
  57.     temperature += incr_temperature;
  58.   }
  59. }
  60.  
  61. /**
  62.  * convert from fahrenheit to celsius
  63.  */
  64. void f2c() {
  65.   float temperature = initial_temperature;
  66.   while (temperature <= final_temperature) {
  67.     float c = (temperature -32.0) * 5.0 / 9.0;
  68.     if (verbose > 0) { 
  69.       cout << temperature << "°F = " << c << "°C" << endl;
  70.     }
  71.     temperature += incr_temperature;
  72.   }
  73. }
  74.  
  75. /**
  76.  * main function
  77.  */
  78. int main(int argc, char *argv[]) {
  79.   // declare command line parser
  80.   clap::CommandLineParser cl(program_name, program_desc,
  81.     argc, const_cast<char **>( argv ));
  82.    
  83.   try {
  84.    
  85.     // --------------------------------------------------------------
  86.     // add options 
  87.     // --------------------------------------------------------------
  88.    
  89.     // verbose level (0 = silent)
  90.     cl.add_natural("verbose", 'v', &verbose,
  91.         "verbose level, chose between 0 (silent) or 1, default is 1");
  92.        
  93.     // unit of initial and final temperatures  
  94.     cl.add_options("unit", 'u', &unit_id,
  95.         &unit_options, "unit of initial and final temperature");
  96.    
  97.     // option with trigger, increment
  98.     cl.add_float("incr-temperature", 'c', &incr_temperature,
  99.         "temperature incrementation", &trigger_temperature);       
  100.  
  101.     // options that need to be set and use trigger
  102.    
  103.     // set initial temperature           
  104.     cl.add_float("initial-temperature", 'i', &initial_temperature,
  105.         "initial temperature", &trigger_temperature)->set_needed();
  106.        
  107.     // set final temperature   
  108.     cl.add_float("final-temperature", 'f', &final_temperature,
  109.         "final temperature", &trigger_temperature)->set_needed();
  110.                
  111.     // --------------------------------------------------------------
  112.     // parse command line arguments
  113.     // --------------------------------------------------------------
  114.     cl.parse();
  115.      
  116.     // --------------------------------------------------------------
  117.     // your code here
  118.     // --------------------------------------------------------------
  119.     if (unit_id == 0) {
  120.       c2f();
  121.     } else {
  122.       f2c();
  123.     }  
  124.    
  125.   } catch(exception& e) {
  126.     cl.report_error(e);
  127.   }
  128.  
  129.   return 0;
  130. }
  131.  
./bin/temperature.exe -h
NAME
	temperature.exe - convert temperatures between celsius and 
	fahrenheit 

SYNOPSIS
	temperature.exe [OPTIONS] ... 

DESCRIPTION

	--final-temperature=FLOAT or -f FLOAT
		final temperature 

	--help or -h
		help flag, print synopsis 

	--incr-temperature=FLOAT or -c FLOAT
		temperature incrementation 

	--initial-temperature=FLOAT or -i FLOAT
		initial temperature 

	--output=STRING or -o STRING
		output file, used to redirect output 

	--unit=VALUE or -u VALUE
	  where value=celsius, fahrenheit
		unit of initial and final temperature 

	--verbose=NATURAL or -v NATURAL
		verbose level, chose between 0 (silent) or 1, default is 1 

1.2.13. Variables d'environnement

On peut récupérer le contenu des variables d'environnement grâce à la fonction getenv et utiliser setenv ou putenv pour définir une variable :

#include <stdlib.h>
char *getenv(const char *name);
int setenv(const char *name, const char *value, int overwrite); // overwrite=1 pour écrire
int putenv(char *string); // ici string est de la forme name=value

Voici un exemple qui extrait les répertoires depuis le PATH, puis liste les fichiers de chaque répertoire.

Note : les modifications de l'environnement sont locales au programme et donc perdues après arrêt du programme.

Afficher le code    ens/inra/c1_getenv.cpp
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <sys/types.h>
  5. #include <dirent.h>
  6. #include <errno.h>
  7.  
  8. #define BUFFER_MAX_SIZE 200
  9. #define SIZE(x) ((x) > BUFFER_MAX_SIZE) ? BUFFER_MAX_SIZE : x
  10.  
  11. const char *MYLIB_PATH = "MYLIB_PATH=/home/richer";
  12.  
  13. /**
  14.  * list files in directory identified by its name
  15.  */
  16. void find_files(char *dir_name) {
  17.   DIR *dp;
  18.   struct dirent *ep;
  19.  
  20.   dp = opendir (dir_name);
  21.   if (dp != NULL) {
  22.     while (ep = readdir (dp)) {
  23.       if (!((strcmp(ep->d_name, ".") == 0) ||
  24.           (strcmp(ep->d_name, "..") == 0))) {
  25.         printf("- %s\n", ep->d_name);
  26.       }
  27.     }
  28.     (void) closedir (dp);
  29.   } else {
  30.     perror ("Couldn't open the directory");
  31.   }
  32. }
  33.  
  34. /**
  35.  * main function
  36.  */
  37. int main(int argc, char *argv[]) {
  38.   char *path, *pbegin, *pend;
  39.   char buffer[BUFFER_MAX_SIZE + 1];
  40.  
  41.   path = getenv("PATH");
  42.  
  43.   // get all paths and find files
  44.   /*
  45.   pbegin = path;
  46.   pend = strchr(pbegin, ':');
  47.  
  48.   while (*pbegin != '\0') {
  49.     if (pend == NULL) {
  50.       strcpy(buffer, pbegin);
  51.     } else {
  52.       int size = pend-pbegin;
  53.       strncpy(buffer, pbegin, SIZE(size));
  54.       buffer[pend-pbegin] = '\0';
  55.     }
  56.    
  57.     printf("===========================\n");
  58.     printf("directory %s\n", buffer);
  59.     printf("===========================\n");
  60.    
  61.     find_files(buffer);
  62.    
  63.     if (pend == NULL) break;
  64.    
  65.     pbegin = ++pend;
  66.     pend = strchr(pbegin, ':');
  67.    
  68.   }
  69.   */
  70.  
  71.   // or simplified version using strtok
  72.   char *p = strtok(path, ":");
  73.   while (p != NULL) {
  74.     printf("===========================\n");
  75.     printf("directory %s\n", p);
  76.     printf("===========================\n");
  77.    
  78.     find_files(p);
  79.    
  80.     p = strtok(NULL, ":");
  81.   }
  82.  
  83.   // play with setenv a
  84.    
  85.   if (!setenv("MYLIB_PATH", "/home/richer", 1) < 0) {
  86.     fprintf(stderr, "could not set MYLIB_PATH with setenv !!!!\n");
  87.   }
  88.  
  89.   if (putenv(const_cast<char *>(MYLIB_PATH))!=0) {
  90.     fprintf(stderr, "could not set MYLIB_PATH with putenv => %d\n%s !!!!\n",
  91.     errno, strerror(errno));
  92.   }
  93.  
  94.   system("echo $MYLIB_PATH");
  95.   system("/home/richer/public_html/ens/inra/test_mylib.sh");
  96.  
  97.   return EXIT_SUCCESS;
  98. }
  99.  
could not set MYLIB_PATH with setenv !!!!
could not set MYLIB_PATH with putenv => 2
No such file or directory !!!!
/home/richer
/home/richer/public_html/ens/inra/test_mylib.sh: 2: /home/richer/public_html/ens/inra/test_mylib.sh: MYLIB_PATH: not found
MYLIB_PATH =