FAQ CConsultez toutes les FAQ

Nombre d'auteurs : 28, nombre de questions : 175, création le 11 janvier 2013 

 
OuvrirSommairePointeurs, tableaux et chaînes de caractèresLes pointeurs

Chaque donnée en mémoire a une "adresse". Cette adresse permet au processeur de retrouver la donnée et de demander sa lecture ou son écriture. Un pointeur est une variable qui contient une adresse (ça peut être l'adresse d'une variable, celle d'une fonction ou encore celle d'un élément d'un tableau, etc.). Par abus de langage, on utilise aussi parfois le mot "pointeur" pour désigner une "adresse".

Mis à jour le 2004-09-20  par Bob

Il est difficile de fournir une liste exhaustive des cas où on doit on devrait utiliser les pointeurs mais voici quelques exemples :

  • Chaque fois qu'on veut passer "une variable" à une fonction.
  • Chaque fois qu'on veut passer un objet de taille trop importante à une fonction. En effet, il est moins coûteux (en termes temps d'exécution et de consommation de mémoire) de passer l'adresse de l'objet seulement que de passer une copie de l'objet lui-même (qui peut faire plusieurs octets contre 4 seulement pour une adresse sur un processeur Intel 32 bits par exemple) à la fonction.
  • Chaque fois qu'on veut allouer dynamiquement de la mémoire.
  • Chaque fois que le langage l'impose (par exemple, le nom d'un tableau, sauf lorsqu'il apparaît en argument de l'opérateur sizeof, est toujours converti par le compilateur en l'adresse de son premier élément !).
Mis à jour le 2009-03-23  par Bob

Lien : Comment passer 'une variable' à une fonction ?

NULL est une macro définie dans <stddef.h> (qui est inclus par stdio.h, stdlib.h, etc.) et sert à représenter une adresse invalide. NULL est par exemple utilisé dans les fonctions retournant un pointeur pour indiquer que la fonction a échoué. Affecter NULL à un pointeur sert donc à indiquer que le pointeur est actuellement inutilisé (ou pointe "nulle-part"). La norme stipule que n'importe quelle expression entière vallant 0 ou une telle expression castée en void * représente une telle adresse.

Mis à jour le 2008-07-27  par LFE, gl

Un pointeur sur une structure se déclare comme tout autre pointeur :

 
Sélectionnez
struct base {
   int    a;
   double b;
};
 
struct base  Elem;
struct base * p = &Elem;

Pour accéder au membre de la structure via le pointeur il est nécessaire de déréférencer ce pointeur :

 
Sélectionnez

(*p).a = 5;

Cette écriture pouvant se simplifier en :

 
Sélectionnez

p->a = 5;

(*p).a n'est cependant pas strictement équivalent à p->a car la première déréférence p (lit sizeof(struct base) octets à partir de cette adresse) avant d'entrer dans le champ a alors que p->a accède directement à la mémoire utile. (*p).a est donc non seulement plus long a écrire que p->a mais également moins performant.

Mis à jour le 2008-07-27  par gl, fearyourself, Melem

sizeof(struct data) fournit la taille de la structure alors que sizeof(struct data *) fournit celle d'un pointeur.

Mis à jour le 2008-07-27  par Bob, gl

Le plus simple est de définir dans un premier temps le type de la fonction sur laquelle on veut pointer :

 
Sélectionnez
typedef int F(void); /* F : type de la fonction f. */
 
/* Voici la fonction f en question : */
 
int f(void)
{
    return 1;
}

Un pointeur sur f se déclare donc ainsi :

 
Sélectionnez

F * p = &f;

En fait, le nom d'une fonction (f), lorsqu'il n'est pas utilisé dans sizeof ou avec l'opérateur & ("adresse de"), est toujours converti par le compilateur en &f. Cela signifie qu'on aurait pu également écrire tout simplement :

 
Sélectionnez

F * p = f;

Et enfin, pour appeler f, les deux écritures suivantes sont toutes valides :

 
Sélectionnez
y = p();
y = (*p)();

Sans typedef, p devrait avoir été déclaré ainsi :

 
Sélectionnez

int (*p)(void) = f;

En effet, on veut un pointeur p vers une fonction qui ne prend aucun argument et qui retourne un int. (*p) vaut donc "une fonction" (même si une telle expression, sauf si elle est utilisée comme argument de sizeof, sera toujours automatiquement convertie en l'adresse de la fonction ...). Cette fonction prend void comme argument retourne un int. Autrement dit, (*p)(void) vaut un int d'où int (*p)(void) !

Mis à jour le 2009-06-15  par Melem

C'est tout à fait normal, le test (p == q) compare les deux pointeurs et non le contenu des emplacements qu'ils pointent. Pour comparer le contenu, il faut comparer *p et *q ou utiliser la fonction memcmp.

 
Sélectionnez
int * p, * q, n = 10;
 
/* On suppose ici que malloc ne peut pas echouer */
 
p = malloc(sizeof(int));
q = malloc(sizeof(int));
 
memcpy(p, amp;&n, sizeof(int)); /* Ou simplement *p = n; */
memcpy(q, amp;&n, sizeof(int)); /* Ou simplement *q = n; */
 
/* (*p == *q) mais (p != q) */
 
free(p);
 
p = q;
 
/* Maintenant (p == q) et donc naturellement (*p == *q) */
 
free(p); /* Ou free(q) */
Mis à jour le 2008-07-27  par LFE, Melem

Les opérateurs unaires (*, ++ et --) ont une priorité élevée et sont évalués de droite à gauche.

*p++; retourne la valeur de la zone mémoire désignée par p et incrémente ensuite p.

(*p)++; incrémente quant à lui la valeur de la zone mémoire désignée par *p.

Créé le 2003-01-31  par LFE

Il convient de ne pas confondre les pointeurs constants (le pointeur ne pourra pas être modifié, mais la variable pointée si) et les pointeurs sur constantes (le pointeur pourra être modifié mais pas la variable pointée).

Exemple de pointeur constant
Sélectionnez
char buf[] = "bonjour";
char * const p = buf; /* Le pointeur p est declare constant */
 
p++;    /* Incorrect car on ne peut pas modifier une constante */
p[4]++; /* Correct */
Exemple de pointeur sur des constantes
Sélectionnez
char buf[] = "bonjour";
char const * p = buf; /* Ou const char * p = buf; */
 
p++;    /* Correct */
p[4]++; /* Incorrect car les donnees pointees par p ont ete declarees constantes (const char) */

Il est bien entendu possible de combiner les deux pour créer un pointeur constant sur des constantes :

Exemple de pointeur constant sur des constantes
Sélectionnez
char buf[] = "bonjour";
char const * const p = buf;
 
p++;    /* Incorrect */
p[4]++; /* Incorrect */
Mis à jour le 2006-09-11  par gl

La mémoire n'étant pas typée (le contenu de la mémoire dépend toujours de l'interprétation qu'on en fait), on ne peut pas connaître le type d'un objet pointé par un void *. Si le pointeur est utilisé pour créer une fonction générique, il est nécessaire de rajouter à la fonction un paramètre indiquant la taille ou le type de la variable utilisée :

 
Sélectionnez
typedef enum {
    TYPES_char,
    TYPES_short,
    TYPES_int,
    TYPES_long,
    TYPES_float,
    TYPES_double
} TypePtr;
 
void MyFunction(void * Ptr, TypePtr Type);
Mis à jour le 2008-07-27  par gl, LFE
  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2013 Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Cette page est déposée.