Langage C: probléme avec pointeur

Résolu/Fermé
Hatrix - 14 avril 2009 à 21:55
mamiemando Messages postés 33302 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 3 octobre 2024 - 17 avril 2009 à 11:26
Bonjour,
j'étais excellent au C, mais quand on arrive au cours de pointeur je me suis perdu!!
bon je sais pas ce que c'est un pointeur exactement, ce que je sais c'est un pointeur c comme une boite avec un numéro, ce numéro est l'adresse qui est désigné par p par exemple, et le contenue de cette boite qui est désigné par *p.
je voudrais savoir c'est koi la différence entre un pointeur est une variable, j'arrive pas a comprendre ca sert a quoi l'adress du pointure ,
pouvez vous me donnez quelques exemple de programme simple svp?
Merci d'avance
A voir également:

10 réponses

30MillionsD'amis
14 avril 2009 à 23:10
Slt,

L'intérêt du pointeur par exemple se découvre surtout dans les manipulations de tableaux - en affectant un pointeur à l'adresse du premier élément d'un tableau, tu parcours ensuite ton tableau en incrémentant ton pointeur :

int t[10]; --> ton tableau
for ( i=0 ; i<10 ; i++) --> initialisation tableau
t[i] = i * i;
int* x; --> déclaration pointeur
x=&t[0]; --> initialisation du pointeur vers l'adresse du premier élément du tableau
for ( i=0 ; i<10 ; i++)
{
cout << *x << " "; --> affichage valeur du pointeur <==> valeur entrée courante du tableau
x++; --> incrémentation du pointeur
}

Autre exemple :

Le pointeur a permis ainsi d'envoyer la chienne soviétique Laika en orbite dans une capsule au début des années 60... elle s'en souvient encore Laika de son pointeur car elle tourne encore à cette heure ... "woouufff !!..." (:>).

Cordialement.
2
mamiemando Messages postés 33302 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 3 octobre 2024 7 793
15 avril 2009 à 00:23
Un pointeur est juste une adresse. Le fait de typer un pointeur permet de débloquer les features suivantes :

1) L'opérateur * appliqué à une adresse permet d'interpréter ce qui se trouve à cette adresse conformément au type du pointeur. Par exemple si tu as un "int *p", *p est interprété comme un int. L'opérateur & appliqué à une variable permet de récupérer l'adresse de cette variable (objet ou type de base).

2) Cela permet également de définir de combien on se décale quand on l'incrémente/décrémente (par exemple ++p ajoute à l'adresse de p sizeof(int) dans mon exemple).

Concrètement, si p n'est pas initialisé ou contient une adresse invalide (par exemple NULL) on aboutit le plus souvent à une erreur de segmentation. Dès qu'on applique l'opérateur * ou -> à un pointeur, il faut donc s'assurer au préalable que ce pointeur contient toujours une adresse valide.

Tout pointeur initialisé avec un malloc (ou autre fonction d'allocation mémoire) doit être libéré par la suite ou un free (et uniquement ces pointeurs). L'avantage d'allouer de cette manière une variable en mémoire est que la variable perdure au delà du scope (= horizon = paire d'accolade) dans lequel elle est allouée (et c'est d'ailleurs pour ça qu'au bout d'un moment il faut la libérer).

Pour plus de détails :
http://www.commentcamarche.net/forum/affich 11612519 algorithmque et programmation#1

Bonne chance
1
bon merci a vous deux, j'ai une idéé claire sur les pointeur, alors le pointeur est une adress, par l'opérateur * on peut voir le contenue de cette adress, les pointeur sont souvent utiliser pour parcourire des tableau, si le pointeur est incrémenté ill avance par le nombre d'oct, sa depend de chaque variablee (sizeof) et il faut libérer la case mémoire du pointeur aprés l'avoir utiliser, on libére chaque pointeur qui n'a pas de valeur (NULL).
ditez moi si j'ai dit quelque chose fausse,
Merci a vous, j'ai repris le gout du C :P

Cordiallement
0
mamiemando Messages postés 33302 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 3 octobre 2024 7 793
15 avril 2009 à 19:51
Dans l'ensemble tu as compris mais il y a quelques imprécisions.

- le pointeur est une adresse Oui !
- par l'opérateur * on peut voir le contenu de cette adresse Oui !
- les pointeur sont souvent utiliser pour parcourir des tableaux Non


Plus précisément les tableaux sont un confort syntaxique mais n'existent pas à proprement parler en C. p[i] correspondant à *(p+i). Si p est un pointeur de type plop *, alors (p+i) est situé à l'adresse p + i*sizeof(plop). Tu noteras en particulier que p[0] ou *p désignent la même chose.

Quand on écrit :
int p[100];

... on alloue un bloc mémoire de 100*sizeof(int) et on stocke l'adresse de ce bloc dans p (qui n'est jamais qu'un int *, donc une adresse).

Si le pointeur est incrémenté il avance par le nombre d'octet, ça dépend de chaque variable (sizeof) Non

Ça dépend du type du pointeur incrémenté/décrémenté. Ainsi tu peux tout à fait écrire :
#include <stdio.h>

int main(){
    unsigned i;
    int pi[100]; // j'alloue une plage de 100*sizeof(int) ==  400*sizeof(char)
    char *pc = (char *) pi;
    for(i=0;i<50;++i,++pc) *pc = 'z';
    *pc = '\0';
    printf("%s\n",(char *)pi);
    return 0;
}

Alors ok c'est une manière bien tordue d'allouer un buffer et il aurait été plus logique de déclarer un char buffer[400]; ce qui aurait éviter de faire des casts. Cet exemple sert juste à te montrer que les opérations sur les pointeurs ne dépendent pas de la manière dont la variable a été allouée ou déclarée, mais uniquement du type du pointeur. La seule chose dont il faut s'assurer c'est qu'on ne déborde pas du tableau (ce qui est le cas ici puisqu'on ne parcours que 50 octets sur les 400 alloués).

- il faut libérer la case mémoire du pointeur après l'avoir utilisé, on libère chaque pointeur qui n'a pas de valeur (NULL) Non

Il faut uniquement libérer avec un free() les plages mémoires allouées avec un malloc ou toute autre fonction d'allocation mémoire. Il ne faut désallouer cette plage mémoire qu'une seule fois. En général, qui dit malloc dit un free correspondant.

Il est clair que faire un free(NULL) va planter car c'est une adresse à laquelle le programme n'a pas pu allouer un bloc mémoire.

Si tu regardes les pointeurs pi et pc dans mon exemple, ils n'ont pas été initalisés via une fonction d'allocation mémoire (malloc ...), c'est directement l'adresse d'une variable. Dans ce cas, pas de free, les variables locales associées seront désallouées à la fin du scope.
{
  int x;
  int *p = &x;
} // x <-- est désalloué

A contrario :
{
  int *x;
  {
    int *p = (int *)malloc(100*sizeof(int));
    x = p;
  } // p est désalloué car c'est une variable locale, mais pas ce qui s'y trouve
  //...
  free(x); // ce qui se trouve à l'adresse x (anciennement p) est désalloué
} // x est désalloué en tant que variable locale du scope


Bonne chance
1
30MillionsD'amis
15 avril 2009 à 20:06
Slt à tous,

"les tableaux sont un confort syntaxique mais n'existent pas à proprement parler en C"

Bon courage Hatrix !! (:>)...

Mes hommages Mam'Mamie.
Cordialement.
0
mamiemando Messages postés 33302 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 3 octobre 2024 7 793
15 avril 2009 à 23:22
donc on n'utilise pas de free si on a pas utilisé malloc! Oui ! (malloc ou calloc, ou tout autre fonction d'allocation mémoire dynamique).

si le sizeof(int)= 8 oct, donc le pointeur incrémente de 8 oct! Oui (à ceci près que que sizeof(int) = 4 octets soit 32 bits sur une architecture 32 bits)

donc si les tableau sont une imagination pour facilité l'utilisation, Oui

donc le tableau est un char?! Non !

Quand tu écris :
int p[100];

celà signifie que tu alloues un bloc de 100 entiers en tant qu'espace. Cet espace débute à l'adresse p. En mémoire... c'est juste un espace mémoire. La notion de type ne permet au compilateur que de savoir dimensionner un espace mémoire. Par exemple un int est encodé sur 32 bits, un char est codé sur 8 bits etc...

Une fois alloué, tu peux utiliser cet espace mémoire comme tu le souhaites. Par exemple dans l'exemple de mon message précédent, pi était à priori un espace prévu pour accueillir 100 entiers, or avec pc je l'ai détourné pour en faire un espace dans lequel stocker des caractères. Mais en soi, c'est juste un espace mémoire.

Bon mais attention, ne me fais pas dire ce que je n'ai pas dit. Si tu as besoin de stocker un tableau de caractère il est plus logique de déclarer un char[100] ou d'allouer avec un malloc un espace mémoire pointé par un char * :
char *buffer = (char *)malloc(sizeof(char)*100);
...
free(buffer);

À partir de là, tu dois avoir compris qu'écrire char buffer[100] revient à allouer un bloc contigu de mémoire dont la taille fait 100*sizeof(char) et que cet espace est situé à l'adresse p. Ecrire par la suite p[i] revient à évaluer (vu que p est un char *) l'espace de taille sizeof(char) situé à l'adresse p décalée i*sizeof(char).
B o n j o u r \0
0 1 2 3 4 5 6 7

Ici :
p[0] = 'B'
p[1] = 'o'
...

Maintenant pour achever de te convaincre que c'est de la mémoire et vu que sizeof(int) = 4 * sizeof(char), si tu écris :
int * pi = p;

... alors écrire pi[0] revient à évaluer les bits formés par la concaténation des bits formés par les caractères "Bonj" (c'est-à-dire p[0],p[1],p[2],p[3]) en tant qu'un entier. Ok ça va faire une valeur en carton pâte, mais d'un point de vue mémoire c'est la même chose.

Mais bon là je tombe un peu dans le détail, concrètement tu n'as jamais à te poser ce genre de question.

Ce que tu dois retenir c'est que :

- déclarer un tableau équivaut à déclarer un bloc de mémoire contigu
- écrire p[i] équivaut à écire *(p+i)
- qui dit malloc (ou autre fonction d'allocation mémoire) dit free (et uniquement dans ce cas)
- évaluer ce qui se trouve à une adresse mémoire invalide provoque une erreur de segmentation (c'est typiquement ce qui se passe quand tu accèdes à la cases 101 d'un tableau de 100 éléments, tu sors de ton espace mémoire).
- une variable locale est désallouée à la fin du scope
- un paramètre de fonction est une recopie de la variable passée lors de l'appel de cette fonction.

J'en profite pour faire une digression sur ce dernier point :
#include <stdio.h>

int f(int x){ // ce x est une recopie du x du main
  ++x;
  printf("%d\n",x); // la copie est incrémentée
}

int f2(int *x){ // la valeur recopiée est l'adresse de x
  ++(*x); // ... donc ici, c'est bien la variable x du main que je modifie
  printf("%d\n",*x);
}

int main(){
  int x = 3;
  f(x); // écrit 4 (la copie est incrémentée)
  printf("%d\n",x); // écrit 3, car la variable x du main vaut toujours 3
  f(*x); // écrit 4 (x est bien incrémenté)
  printf("%d\n",x); // écrit 4
  return 0;
}

Si tu as tout compris, tu sais tout ce qu'il est important de savoir sur les pointeurs ;-)
Il reste deux trois notions sur les const à voir mais c'est à peu près tout.

Bonne chance
1

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
eh ben grace a vous j'ai bien compri les pointeur, Merci a tous ceux qui m'ont repondue, speciallement mamiemando! merci bcp, je revient a programmer en C!! :D

Cordiallement
0
mamiemando Messages postés 33302 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 3 octobre 2024 7 793
17 avril 2009 à 01:14
De rien, bonne continuation, et bon courage avec les pointeurs !
0
Char Snipeur Messages postés 9813 Date d'inscription vendredi 23 avril 2004 Statut Contributeur Dernière intervention 3 octobre 2023 1 298
17 avril 2009 à 09:54
donc on n'utilise pas de free si on a pas utilisé malloc!
Au sujet de cette phrase, il n'y aurai quelques exceptions ?
Il me semble qu'il y a des fonctions qui alloue de la mémoire, et qu'il faut donc libérer explicitement.
Par exemple (de mémoire)
pthread_attr_init(....);
qu'il faut détruire dans ce cas là avec une autre fonction.
Sans oublié les éventuelles fonction utilisateurs.
0
mamiemando Messages postés 33302 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 3 octobre 2024 7 793
17 avril 2009 à 10:28
Effectivement mais c'est exceptionnel et quand ça arrive, c'est précisé dans la doc. Un exemple : getline avec un buffer passé à NULL.
http://pwet.fr/man/linux/fonctions_bibliotheques/getline/

Pour les fonctions utilisateurs... c'est vrai mais ça sous entend que ces fonctions appelent un malloc (ou autre fonction qui alloue dynamiquement de la mémoire), donc ça ne remet pas en cause ce que j'ai dit. Dans tous les cas, le programmeur est au courant, soit parce qu'il a codé la fonction, soit parce que c'est indiqué dans le man. Ceci dit il est vrai qu'on a pas forcément en tête l'intégralité du code qu'on a pu écrire.

C'est pour cela qu'il faut une certaine discipline : une allocation interne à une fonction devrait être désallouée au cours (souvent à la fin) du scope dans lequel elle a été effectuée :
{
  char *buffer = (char *) malloc(sizeof(char)*n);
  //...
  free(buffer);
}

Si c'est une fonction qui alloue un objet (structure...) le malloc étant fait dans la fonction qui assure la construction, il est logique de placer la désallocation dans la fonction qui fait office de destructeur. Et en général on choisit pour ces deux fonctions un nom suffisamment explicite pour ne pas le perdre de vue (par exemple new_plop() et delete_plop()) qui seront à leur tour placé soit dans un même scope, soit chacun dans un "constructeur" ou un "destructeur" :
struct plop{
  char *buffer;
};

struct plop new_plop(unsigned n){
  plop p;
  p.buffer = (char *) malloc(sizeof(char)*n);
}

struct delete_plop(plop *p){
  free(p.buffer);
}

int main(){
  plop p = new_plop(255);
  //...
  delete_plop(p);
  return 0;
}

Bonne chance
0
Char Snipeur Messages postés 9813 Date d'inscription vendredi 23 avril 2004 Statut Contributeur Dernière intervention 3 octobre 2023 1 298
17 avril 2009 à 10:38
Merci pour ces précisions.
même si tu as écris n'importe quoi comme exemple :-P (même les meilleurs font des erreur ;-) )
0
mamiemando Messages postés 33302 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 3 octobre 2024 7 793
17 avril 2009 à 10:41
Ah oui pardon :
#include <stdlib.h>

struct plop{
    char *buffer;
};

struct plop new_plop(unsigned n){
    struct  plop p;
    p.buffer = (char *) malloc(sizeof(char)*n);
}

struct plop delete_plop(struct plop p){
    free(p.buffer);
}

int main(){
    struct plop p = new_plop(255);
    //...
    delete_plop(p);
    return 0;
}

Merci Char sniper
0
Char Snipeur Messages postés 9813 Date d'inscription vendredi 23 avril 2004 Statut Contributeur Dernière intervention 3 octobre 2023 1 298
17 avril 2009 à 10:49
Manque plus qu'un return pour new_plop, et je ne dirai plus rien :-D
C'est presque une discussion qu'on pourrai mettre en base de connaissance, avec un peu de mise en forme.
0
mamiemando Messages postés 33302 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 3 octobre 2024 7 793 > Char Snipeur Messages postés 9813 Date d'inscription vendredi 23 avril 2004 Statut Contributeur Dernière intervention 3 octobre 2023
17 avril 2009 à 11:26
Ouais c'est une bonne idée. Les questions sur les pointeurs reviennent souvent. Malheureusement je n'ai pas trop le temps de m'en occuper dans l'immédiat, est-ce que tu peux t'en charger ? Je peux éventuellement faire une passe sur ton document ensuite.
0
slt,
encore merci pour m'avoir répondue, j'ai bien compris ce pointeur!
donc on n'utilise pas de free si on a pas utiliser malloc!
si le sizeof(int)= 8 oct, donc le pointeur incrémente de 8 oct!
donc si les tableau sont une imagination pour facilité l'utilisation, donc le tableau est un char?!
-1
donc on n'utilise pas de free si on a pas utiliser malloc!
cette affirmation est une certitude.
si le sizeof(int)= 8 oct, donc le pointeur incrémente de 8 oct!
donc si les tableau sont une imagination pour facilité l'utilisation, donc le tableau est un char?!

Le tableau est un 'char', NON c'est faux.
"les tableaux sont un confort syntaxique mais n'existent pas à proprement parler en C"
Je ne suis pas d'accord.
La polémique des tableaux en C vient du fait que les tableaux sont définis comme étant une suite d'éléments mais il n'y a aucune indication sur le nombre d'éléments contenus dans ce tableau. Si bien qu'on ne peut passer un tableau comme paramètre, en effet l'appelant ne peut savoir combien mettre d'éléments sur la pile et la fonction appelée, qui aurait reçu le tableau par l'intermédiaire de la pile, ne peut savoir comment dépiler le tableau puisqu'elle n'en connait pas la taille. C'est la raison pour laquelle on passe, à la fonction appelée, un pointeur sur le tableau (donc pas besoin de le copier sur la pile) et la taille de ce tableau. Notez que pour les chaînes de caractères on ne passe pas la taille car, en C, la chaîne de caractères est, par convention, terminée par un '\0', et c'est cette caractéristique qui est exploitée pour connaître la taille de la chaîne.
Donc, en C, les tableaux existent mais leur utilisation, du fait de l'indétermination de la taille, n'est pas la même que pour les autres types de données dont la taille est parfaitement déterminée.
C'est une des principales raisons pour laquelle ont été introduit, en C++, les conteneurs.
0