Exercice sur les Pointeurs

Steve17_17 - Modifié le 1 mars 2024 à 22:01
mamiemando Messages postés 33113 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 15 mai 2024 - 1 mars 2024 à 23:56

Bonjour,

S'il vous plaît, pouvez-vous vérifier si c'est correct et combler certaines lacunes ?

Écrire le code C correspondant aux actions suivantes tout en illustrant si possible par un schéma.


1. Déclarer une variable entière nommée i et un pointeur p vers un entier.

int i;
int *p;

2. Initialiser i à 10 et faire pointer p sur i;

int i = 10;
p = &i;

3. Afficher la valeur de i.

Printf("%d", p);

4. Modifier l'objet pointé de p, en lui affectant la valeur 15

int i = 15;
p = &i;

5. Afficher l'adresse de la variable pointée de p.

Printf("%d", p);

NB: Je n'ai pas su comment faire les illustrations en question.

Modération : merci de mettre le code en forme comme expliqué dans ce tutoriel

2 réponses

Whismeril Messages postés 19041 Date d'inscription mardi 11 mars 2003 Statut Contributeur Dernière intervention 17 mai 2024 912
Modifié le 1 mars 2024 à 22:04

1. Oui

2. Là, tu n'es pas cohérent, tu pars du principe que p est déclaré à l'exercice 1, mais pas i. Si on était dans un seul et même programme, tu aurais une erreur de redéclaration pour i

3. As-tu testé ce code ? Je ne pense pas, tu devrais.

4. Idem, on ne peut pas redéclarer i encore et encore. Mais en plus, ce n'est pas ce qui t'est demandé, tu dois te servir de p pour affecter la valeur 15, pas de i

5. À part la majuscule ok

PS : pour tes prochains post, il faudra faire comme décrit .


0
mamiemando Messages postés 33113 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 15 mai 2024 7 753
1 mars 2024 à 23:56

Bonjour

Un message pour préciser la réponse de Whismeril #1, déjà très complète.

Je reviens concernant la question 4. Pour rappel, un pointeur n'est qu'une adresse mémoire, par exemple 0x12345. Vois ça comme une sorte d'entier, de taille connue (32 bits sur un système 32 bits, 64 bits sur un systèmes 64 bits).

Exercice 4

Dans le réponse proposée dans le message initial :

int i = 15;
p = &i;
  • Ce bout de code signifie : p prend du bloc mémoire qui stocke la valeur de l'entier i. Donc supposons que l'adresse de l'entier i soit 0x12345. Alors la valeur de p est mise à 0x12345.
  • Cependant, dans l'énoncé, il est demandé de modifier l'objet pointé de p, en lui affectant la valeur 15.
    • Cela signifie que tu ne dois pas modifier p, mais ce qui se trouve dans le bloc mémoire pointé par p (c'est à dire, dans le bloc dont l'adresse est 0x12345, et qui correspond donc à la valeur mémorisée pour i)

Les illustrations

Tu dis :

Je n'ai pas su comment faire les illustrations en question.

Je suppose que ce qu'attends ton enseignant, c'est que tu dessines ce qui se passe en mémoire. Donc par exemple, un entier stocké 32 bits correspond à 4 octets, et dans mon exemple :

  • le premier octet est à l'adresse 0x12345
  • le deuxième octet est à l'adresse 0x12346
  • le troisième octet est à l'adresse 0x12347
  • le quatrième octet est à l'adresse 0x12348

À cheval sur ces 4 octets est stocké en binaire la valeur 15. A noter que l'ordre des bits dépend de l'endianess du système d'exploitation (typiquement little endian sur un système moderne), mais je doute que ton enseignant te demande d'aller aussi loin. Donc je suppose que tu te contentes de marquer 15 à l'intérieur de ce bloc de 4 octets) ou carrément i.

L'adresse p est stockée dans un bloc qui fait la taille d'une adresse mémoire (dont la taille dépend du système d'exploitation, typiquement 64 bits sur un système moderne, peut importe ce qui est stocké à cette adresse). Comme tu définis p = &i cela veut dire que sur ce bloc de 8 octets, tu écris 0x12345 ou carrément p. Et pour parfaire le schéma, tu peux dessiner une flèche, qui va de p vers i.

Explications sur les pointeurs

Un pointeur étant une adresse mémoire, en pratique utilisé pour stocker l'adresse d'un autre bloc mémoire. La taille d'un pointeur est connue, constante, et ne dépend que du système (pas du bloc pointé) :

  • Une adresse occupe toujours 64 bits sur un système 64 bits
  • Une adresse occupe toujours 32 bits sur un système 32 bits
  • ...

Un pointeur est généralement typé, ce qui permet de connaître la taille du bloc mémoire qu'il pointe.

La notion de pointeur est liée aux opérateurs suivants :

  • L'opérateur & permet de récupérer l'adresse d'un bloc mémoire nommé par une variable déclarée. Cette adresse est "pointe" le bloc mémoire en question.
  • Quand un pointeur est typé (ce qui est ton cas), le type supposé de ce qui est pointé est déterminé par le type du pointeur. On peut donc traverser le pointeur, car on sait ce qui doit être lu à cette adresse. C'est le rôle de l'opérateur *.

Parlons un peu plus des deux opérateurs qui nous intéressent de manière schématique :

  • & : On peut voir un pointeur p = &i comme la construction d'une flèche qui va de p vers i (d'où le nom de pointeur).
  • * : L'opérateur * sert à traverser à une telle flèche. Dans notre exemple *p permet de traverser p et d'accéder à i. Si p est de type int *, alors on sait le bloc pointé est supposé avoir la taille d'un int.

Et là, on voit quelque chose d'intéressant émerger par rapport aux types mis en jeu par ces opérateurs :

int i = 7;
int * p = &i;
int j = *p;

Donc

  • Avec & : comme p = &i, le type de p a une étoile de plus que i
  • Avec * : comme j = *p, le type de j a une étoile de moins que p

Et ceci reste vrai quel que soit les types mis en jeu (donc ce que j'ai dit pour int reste vrai pour n'importe quel type, hormis void), ce qui signifie qu'on peut parfaitement appliquer plusieurs fois l'opérateur * sur une variable (tant que ça a un sens).

On peut parfaitement écrire :

int i = 7;
int * p = &i;
int ** q = &p;
int *** r = &q;

D'un point de vue schématique, on a créé une chaîne de pointeur, dans laquelle le pointeur r pointe vers q, qui pointe vers p, qui pointe vers i.

On peut donc parfaitement traverser une ou plusieurs de ces flèches (dans le sens des flèches), grâce à l'opérateur *. En outre, toutes les égalités ci dessous sont vraies :

*p == i;
*q == p; **q == i
*r == q; **r == p; ***r == i

À noter que ***r signifie *(*(*(r))).

Quand on écrit ***r, on traverse trois flèches (puisqu'il y a 3 étoiles), r → q,  q→ p, p → i, et donc on retrouve bien que ***r == i.

Remarque bonus

Si on reprend le dernier exemple, on pourrait se demander si comme on peut écrire ***r, on ne peut pas aussi écrire directement int *** r = &(&(&(i))) au lieu de s'embarrasser avec p et q. Alors la réponse est claire, c'est non, les variables intermédiaires p et q sont nécessaires.

C'est d'ailleurs pour ça que quand j'ai parle de &, j'ai bien bien dit qu'il s'appliquait à une variable déclarée. Pour comprendre pourquoi, je te renvoie à un cours de C qui explique comment marche la pile mémoire. L'intuition est que quand les variables p et q sont déclarées, ces étapes intermédiaires restent en mémoire (dans la pile), ce qui ne serait pas le cas si on ne les utilisait pas.

Bonne chance

0