/* formules magiques C pour que tout aille bien au départ */
#include <stdio.h>
#include <stdlib.h>

/* ici on tente d'échanger les paramètres (mais ils sont locaux et l'effet
   est nul sur les valeurs transmises */
void echangerate(int a,int b) {
  int t;
  t = a;
  a = b;
  b = t;
}

/* ici on échange les valeurs pointées */
void echangereussi(int *p1,int *p2) {
  int t;
  t = *p1;
  *p1 = *p2;
  *p2 = t;
}

/* g est une variable globale (statique) de type entier
 */
int g;

int main(int argc,char *argv[]) {
  printf("valeur de  g=%d\n",g);
  printf("adresse de g=%p\n",&g);
  /* i est une variable entière stockée dans la pile
   */
  int i;
  printf("valeur de  i=%d\n",i);
  printf("adresse de i=%p\n",&i);
  /* p est un pointeur vers un entier, i.e. une variable pouvant contenir
     l'adresse d'un entier, i.e. l'adresse d'une zone de stockage pouvant
     contenir en entier. Attention, p est une variable stockée dans la pile...
  */
  int *p;
  /* & est un opérateur préfixe du langage C permettant d'obtenir pour
     TOUTE variable son adresse en mémoire, i.e. l'endroit en mémoire où
     sa valeur est stockée. Les adresses sont typées, l'adresse d'un entier
     est du type "adresse d'entier" qui se note "int *".
  */
  p = &i;
  printf("valeur de  p=%p\n",p);
  printf("adresse de p=%p\n",&p);

  /* on peut allouer dans le tas via la fonction malloc qui prend en
     paramètre le nombre d'octets que l'on souhaite réserver. Attention,
     en C, nous ne savons pas a-priori quelle est la taille d'un entier, cela
     dépend de la machine, du système et du compilateur! La valeur courante
     est 4 octets, mais il n'y a AUCUNE garantie...
     La fonction sizeof permet d'obtenir la taille de la représentation en
     machine d'un type donné.
     Cette fois p "pointe" vers une zone allouée dans le tas; zone pouvant
     contenir la représentation d'un entier.
  */
  p = malloc(sizeof(int));
  printf("pour ma machine un entier nécessite %ld octets\n",sizeof(int));
  printf("valeur de  p=%p\n",p);
  printf("adresse de p=%p\n",&p);

  /* L'opérateur * permet d'obtenir la valeur stockée à une adresse typée
     donnée. * appliqué à un pointeur permet donc d'obtenir la valeur stockée
     à l'adresse contenue dans le pointeur, donc la valeur contenue dans la
     zone mémoire pointée par le pointeur.
  */
  printf("valeur de l'entier pointé par p=%d\n",*p);

  /* un pointeur est doté d'une arithmétique. On peut se déplacer à partir de 
     ce pointeur; le déplacement étant mesurer en nombre de valeur du type
     pointé, i.e. pour une adresse a sur un T et un entier i, a+i représente 
     la i-ème valeur de type T en partant de a. Cela fonctionne comme les
     adresses postales : 3 maisons après le 5 rue Tartempion, où 5 maisons
     avant le 12 rue Machin.
     Cette arithmétique permet donc d'obtenir des tableaux...
     p va d'abord pointer vers un "paquet" d'entiers alloués dans le tas.
  */
  p = malloc(3*sizeof(int));
  /* on observe que les adresses calculées suivantes sont bien distantes
     d'un nombre d'octets égal à la taille de la représentation d'un entier
  */
  printf("Valeur de p+0=%p\n",p+0);
  printf("Valeur de p+1=%p\n",p+1);
  printf("Valeur de p+2=%p\n",p+2);

  /* cette fois on va lire la valeur aux adresses calculées
   */
  printf("Valeur de l'entier en p+0=%d\n",*(p+0));
  printf("Valeur de l'entier en p+1=%d\n",*(p+1));
  printf("Valeur de l'entier en p+2=%d\n",*(p+2));

  /* le C autorise la syntaxe [] qui est strictement équivalente
   */
  p[0] = 10; // <=> *(p+0) = 10;
  p[1] = 20; // <=> *(p+1) = 20;
  p[2] = 30; // <=> *(p+2) = 30;
  printf("Valeur de l'entier en p+0=%d\n",*(p+0));
  printf("Valeur de l'entier en p+1=%d\n",*(p+1));
  printf("Valeur de l'entier en p+2=%d\n",*(p+2));

  /* attention le langage C ne contrôle rien vis-à-vis des "longueurs" de
     tableaux. Les tableaux n'ont d'ailleurs PAS de longueur en C...
     Le langage C fait confiance à l'expertise du programmeur.
     On peut donc "dépasser" la tableau, les effets sont divers et
     non prédictibles...
  */
  p[4] = 40; // ?????
  printf("Valeur de l'entier en p[4]=%d\n",p[4]);
  /* enlever les commentaires pour expérimenter l'erreur ci-dessous
     laquelle est strictement équivalente logiquement à l'erreur
     ci-dessus, mais dont l'effet est autre!
     Bienvenu dans le monde des bugs... Celui-ci est celui dit du
     pointeur fou.
  */
  // p[400000] = 40; // ?????
  // printf("Valeur de l'entier en p[400000]=%d\n",p[400000]);

  int a,b;
  a = 111;
  b = 222;
  printf("avant echangerate   a=%d b=%d\n",a,b);
  echangerate(a,b);
  printf("apres echangerate   a=%d b=%d\n",a,b);

  /* ATTENTIOn ici on échange les valeurs pointées, on transmet donc les
     adresses (qui elles ne sont pas échangées)...
  */
  echangereussi(&a,&b);
  printf("apres echangereussi a=%d b=%d\n",a,b);
}