Langage C

les pointeurs en langage c

Un pointeur est une variable spéciale qui peut contenir l'adresse d'une autre variable.

En C, chaque pointeur est limité à  un type de données. Il peut contenir l'adresse d'une variable de ce type.

Si un pointeur P contient l'adresse d'une variable A, on dit que 'P pointe sur A'.

Deux manières d'utiliser une variable :

Par son nom => adressage direct

Compilateur réserve de la mémoire pour lavariable

Exemple:

int x =17;

Par son adresse => adressage indirect

une variable de type pointeur contientl'adresse d'une autre variable

Le lien entre pointeur et la variable pointée est géré par le programmeur

Exemple

int x =17;

int * px 

px=&x; // px pointe vers x

1. déclaration d'un pointeur

type * nom_du_pointeur;

Exemples

char *buffer;
int *x;
float *y;

Très important: C'est au programmeur d'initialiser le pointeur

2. initialisation d'un pointeur

Il faut lui fournir l'adresse d'unevariable existant en mémoire

Il doit pointer vers une zonemémoire réservée par le compilateur comme étant une variable

utilisation de l'opérateur d'adressage &

int f;
int *pf,*pg;
pf = &f; // création du lien entre le pointeuret la variable
pg =(int*)malloc(sizeof(int)); // création du lien entre  pg etl'espace mémoire réservé
                                                                // allocation dynamique d'une variablede type int
.
.
free(pg); // libération de l'espace réservé

3. Utilisation des pointeurs

Les pointeurs ont un grand nombre d'intérêts : 

  • Ils permettent de manipuler de façonsimple des données pouvant être importantes (au lieu de passer à une fonctionun élément très grand (en taille) on pourra par exemple lui fournir un pointeurvers cet élément...) 
  • Les tableaux ne permettent de stockerqu'un nombre fixé d'éléments de même type. En stockant des pointeurs dans lescases d'un tableau, il sera possible de stocker des éléments de taille diverse,et même de rajouter des éléments au tableau en cours d'utilisation (la notionde tableau dynamique) ce qui n’est pas possible pour les tableauxstatiques. 
  • Il est possible de créer des structureschaînées.

Après (et seulement après) avoirdéclaré et initialisé un pointeur, il est possible d'accéder aucontenu de l'adresse mémoire pointée par le pointeur grâce à l'opérateur '*'

Si P un pointeur, on doit distinguer *P et &p :  &P contient l’adresse de la variable dont le pointeur P pointe, et *P permet d’accéder à la valeur de la variablesur laquelle pointe P.     

Exemple

int*P ;
int A=10 ;
P=&A ;                                         /*le pointeur P pointe sur A*/
 *p=*p+1 ;                                  /*équivalent à  A=A+1*/
*p=5 ;                                           /*équivalent à A=5 */

4. Les opérations élémentaires sur les pointeurs

Priorité de * et &

Les opérateurs * et & ont la même priorité que les autres opérateurs unaires (la négation !, l'incrémentation ++, la décrémentation --). Dans une même expression, les opérateurs unaires *, &, !, ++, -- sont évalués de droite à gauche. 

Exemple

Après l'instruction 

    P = &X;

Les expressions suivantes, sont équivalentes: 

   Y = *P+1       =>     Y = X+1 
  *P = *P+10   =>     X = X+10 
  *P += 2           =>.    X += 2 
  (*P)++             =>.    X++ 

Dans le dernier cas, les parenthèses sont nécessaires 

On peut uniquement affecter des adresses à un pointeur Seule exception La valeur numérique 0(zéro) est utilisée pour indiquer qu'un pointeur ne pointe 'nulle part'. 

int *P;
P = 0;

Soit P1 et P2 deux pointeurs sur int, alors l'affectation 

P1 = P2;

Copie le contenu de P2 vers P1. P1 pointealors sur le même objet que P2.

5. Lien entre le nom d'un tableau à 1 dimension et les pointeurs

Les pointeurs et les tableaux sontconceptuellement très similaires en C

Nom du tableau = adresse du premier élément du tableau

nom_tableau[i] peut s'écrire *(nom_tableau+i)

En simplifiant, nous pouvons retenir quele nom d’un tableau est un pointeur constant sur le premier élément du tableau.

Exemples:

En déclarant un tableau A de type int et unpointeur P sur int,

int A[10];
int *P;

L’instruction  P = A; est équivalente à P =&A[0];

Si P pointe sur une composante quelconqued'un tableau, alors P+1 pointe sur la composante suivante

Généralement P+i pointe sur la i-ième composant devant P, Ainsi, après l'instruction, 

P = A; Le pointeur P pointe sur A[0], et 

    *(P+1)          désigne le contenu de A[1]  

    *(P+2)          désigne le contenu de A[2] 

       ...                ... 

    *(P+i)           désigne le contenu de A[i]

Puisque le nom tableau est un pointeur constant sur le premier élément on peut écrire :

    *(A+1)          désigne le contenu de A[1]  

    *(A+2)          désigne le contenu de A[2] 

       ...                ... 

    *(A+i)           désigne le contenu de A[i]

Remarque importante :

Il existe toujours une différence essentielle entre un pointeur et le nom d'un tableau: 

  • Un pointeur est une variable,  donc des opérations comme P = A ou P++ sont permises. 
  • Le nom d'un tableau est une constante, donc des opérations comme A = P ou A++ sont impossibles.

Exercice 1 :

Ecrire un programme qui initialise les éléments du tableau T à 1 (version sans pointeur)

/*version sans pointeur*/
#include<stdio.h>
main(){ 
int T[10];
int i;
for(i=0;i<10;i++)
T[i]=1; 

Exercice 2 :

Reécrire lemême programme en utilisant les pointeurs.

/*version  avec  pointeur*/
#include<stdio.h>
main(){
intT[10];
int *P;
int i;
P=T;
for(i=0;i<10;i++)
*(P+i)=1;               /*OU  *(T+i)=1*/
}
/* ce programme donne le même résultat*/
#include<stdio.h>
main(){
int T[10];
int *P;
for(P=T;P<T+10;P++)
*P=1; 
}

6. Lien entre le nom d'un tableau à 2 dimension et les pointeurs

un tableau 2d est un tableau detableau de dimension 1

  • On peut donc dire que le nom d'untableau 2d est l'adresse d'un tableau d'adresse de tableaux de dimension 1
  • On dit aussi qu'il s'agit d'unpointeur de pointeurs

Exemple:

int TAB[6][7];
int **p;      // déclaration d'un pointeur de pointeur
p = TAB;  // initialisation du pointeur de pointeur

On suppose T a deux dimensions défini comme suit: 

    int T[3][4] =     {{ 0, 1, 2, 3}, {10,11,12,13}, {20,21,22,23}};

Le nom du tableau T représente l'adresse du premier élément du tableau et pointe sur le tableau T[0] qui a les valeurs: {0,1,2,3}

L'expression (T+1) est l'adresse du deuxième élément dutableau et pointe sur T[1] qui a les valeurs:  {10,11,12,13}

Au sens strict du terme, un tableau à deux dimensions est un tableau unidimensionnel dont chaque composante est un tableau unidimensionnel. Ainsi, le premier élément de la matrice T est le vecteur {0,1,2,3}, le deuxième élément est {10,11,12,13} et ainsi de suite


Exercice :

Ecrire un programme qui initialise un tableau à deux dimensions. T[6][5]

Méthode 1:

#include<stdio.h>
main()
{
    int T[6][5];
    int *P[6],i,j,ligne,col;
    printf("introduirele nombre de ligne ");
    scanf("%d",&ligne);
    printf("introduirele nombre de colonne ");
    scanf("%d",&col);
    for(i=0;i<ligne;i++)
    {
        P[i]=T[i];
        for(j=0;j<col;j++)
           *(P[i]+j)=1;
     }
     for(i=0;i<ligne;i++)
       for(j=0;j<col;j++)
         printf("%d  ",*(P[i]+j)); //  printf("%d  ",T[i][j]);
 }

Pour cet exercice nous avons utilisé un tableau des pointeurs *P[6] chaque pointeurP[i] pointe sur une ligne T[i] .

Méthode 2

Dans la mémoire les éléments d’un tableau à deux dimensions sont adjacents, on peut utiliser un pointeur qui pointe sur le premier élément du tableau et ensuite déplacer ce pointeur sur les autres éléments du tableau. 

#include<stdio.h>
main()
{
    int T[6][5];
    int *P,i,j,ligne,col;
    printf("introduire le nombre deligne ");
    scanf("%d",&ligne);
    printf("introduire le nombre decolonne ");
    scanf("%d",&col);
    P=T[0];
    for(i=0;i<ligne*col;i++)
          *(P+i)=1;
     for(i=0;i<ligne*col;i++)
           printf("%d  ",*(p+i)); //ou *(p++)
}

7. Transmission des paramètres d’une fonction

7.1. Transmission par valeur 

Ecrire une fonction permuter qui permet de permuter deux entiers, puis écrire le programme appelant cette fonction.

void permuter(intX,intY){
     int temp;
     temp=X;
     X=Y;
     Y=temp;
}
main(){
      int A,B;
      A=4;
      B=5;
     printf("avantl'appel de la fonction permuter A=%d, B=%d",A,B);
     permuter(A,B);    
     printf("\n  apresl'appel de la fonction permuter A=%d, B=%d",A,B);
}

Lors de l'appel, les valeurs de A et de B sont copiées dans lesparamètres X et Y. PERMUTER échange bien le contenu des variables locales X et Y, mais les valeurs de A et Brestent les mêmes on dit que A et B sont transmisespar valeur.  donc après exécution A=  4  et B= 5

7.2. Transmission par adresse

Reprenons le même exercice, pour que la fonction permuter puisse échanger les valeurs de A et B on doit lui renvoyer  les adresses de A et B on dit que A et B sont transmises par adresse

void permuter(int*X,int*Y){
     int temp;
     temp=*X;
     *X=*Y;
     *Y=temp;
}
main(){
      intA,B;
      A=4;
      B=5;
      printf("avantl'appel de la fonction permuter A=%d, B=%d",A,B);
      permuter(&A,&B);    
      printf("\n  apresl'appel de la fonction permuter A=%d, B=%d",A,B);
}

8. Allocation de la mémoire 

8.1. Déclaration statique des données :

Si le nombre de case à réserverétait déjà connu pendant la compilation. Nous parlons alors de la déclarationstatiquedes variables. 

Exemple:

                                                     /*1 case = 1 octet*/
int A ;                                          /*réservation de 4 octets*/
float B,C;                                /*réservation de 8  octets */
short D[10][20];                  /*réservation de 400 octets */
int T[5] ;                                  /*réservation de 20 octets*/

Souvent nous utilisons des données dontne nous pouvons pas prévoir le nombre, ce qui nous amène à réserver un espacemaximale, cela entraîne un gaspillage d’espace mémoire.

Par exemple on veut faire un programme qui calcule la somme des valeurs introduites par l’utilisateur, le nombre des valeurs  est déterminé par l’utilisateur

main(){
   float T[100], som;
   int N_element, i;
   printf("le nombre des valeurs à saisir ?");
   scanf("%d", &N_element);
   for (i=0;i<N_element;i++){
        printf("\nintroduire lavaleur N %d ",i);
        scanf("%f",&T[i]);  
   }  
   som=0;
   for (i=0;i<N_element;i++)
         som+=T[i];
   printf("la somme des valeurs introduites : %f",som);  }

On charge les valeurs dans un tableau de 100 éléments on suppose que l’utilisateur a introduit  5 comme nombre des valeurs, 95 des espaces de float sont réservés inutilement dans la mémoire.

8.2. Allocation dynamique de mémoire 

Un moyen d’éviter ce gaspillage de mémoire est de réserver l’espace mémoire lors de l’exécution du programme cette procédure s’appelle allocation dynamique mémoire.

La fonction malloc et l'opérateur sizeof  

  • La fonction malloc de la bibliothèque <stdlib> nous aide à localiser et àréserver de la mémoire au cours d'un programme par exemple malloc(40) réserve 40 octets dans la memoire.
  • La fonction malloc retourne un pointeur générique (void *).
  • sizeof permet de récupérer la taille d’une variable ou un type de données

Exemple 

Apres les déclaration :

int A ;
int T[4] ;
char B ;

Nous obtenons les résultats suivants : 

sizeof(A) ;          /*retourne 4 :variable de type int occupe 4 case dans la mémoire */
sizeof(B)          /*retourne 1: variable de type char occupe 1 case dans la mémoire */
sizeof (T)             /*retourne 16 : 4 x 4 case*/
sizeof(int)            /*retoune 4*/

En utilisation de la fonction malloc on peut économiser l’espace mémoire utiliseé par le programme précèdent de la somme. Le programme précédent deviendra :

main(){
   float *T, som;
   int N_element, i;
   printf("le nombre des valeurs à saisir ?");
   scanf("%d",&N_element);
   T=(float*) malloc(sizeof(float)*N_element); 
   for (i=0;i<N_element;i++){
       printf("\nintroduire lavaleur N %d ",i);
       scanf("%f",&T[i]);  
   } 
   som=0;
   for (i=0;i<N_element;i++)
        som+=T[i];
   printf("lasomme des valeurs introduites : %f",som);     
}    
  • Au lieu de déclarer un tableau de nombre des éléments fixes nous avons déclaré un pointeur de type float. En suite la fonction malloc génère dynamiquement ‘N_element’ éléments de type float, et retourne l’adresse du premier élément, que le pointeur T recevra
  • La fonction malloc retourne un pointeur générique (void *), on doit donc le convertir en pointeur de type float (float*).

„ 8.3. La fonction free

Si nous n'avons plus besoin d'un bloc demémoire que nous avons réservé à l'aide de malloc, alors nous pouvons le libérer àl'aide de la fonction free de la bibliothèque <stdlib>.

Par exemple à la fin du programme précèdent on peut ajouter l’instruction suivante :

free(T) pour libérer l’espace du tableau.

Remarque :

Si nous ne libérons pas explicitement la mémoire à l'aide de free, alors elle est libérée automatiquement à la fin du programme.

Partager ce cours avec tes amis :
Rédigé par Mostafa Sedoki
Computer science teacher and the founder of the e-learning platform "developpement-informatique.com", my mission is to provide high-quality courses for free to all computer science students and teachers

Cours Similaires :