Téléchargement Gratuit ZoneTelechargement

..

Ement appli break free

Telecharger ement appli break free

Aide


Vous devez vous inscrire afin de télécharger
Veuillez créer un compte gratuitement sur Torrent9 pour accéder aux téléchargements illimités et au streaming !

Pourquoi ce document ? Les débutants se prennent souvent la tête avec les pointeurs, un sujet considéré comme "avancé" et "difficile".

Il y a souvent une bonne raison à cela : dans de trop nombreux cours et tutoriels en ligne (je vais éviter de donner des noms pour me fâcher avec personne), c'est très mal expliqué, au mauvais moment, et mélangeant différents aspects : ce que c'est, les opérations qu'on peut faire dessus en C, et l'usage qu'on en a pour constituer des structures de données (chaînages), avec les difficultés algorithmiques qui s'en suivent.

Le problème, c'est pas du tout que les pointeurs soient "difficiles", c'est qu'on s'en sert pour faire beaucoup de choses (presque tout, en fait, quand on programme en C), et donc qu'il faut toucher à pas mal de choses qui font usage des pointeurs. C'est un sujet riche.

Orientation : Je n'ai pas voulu faire un cours de C pour débutants complets. Je suppose que le lecteur a commencé à apprendre C (variables, tableaux, fonctions, boucles, structures), et qu'il veut des explications sur les pointeurs : le sujet est assez vaste comme ça, sans avoir besoin de reprendre tout à zéro.

Contact : Vous pouvez me contacter ([email protected]) pour toutes remarques, critiques, questions, propositions (honnêtes ou pas), contributions, etc.

Copyright : il faut que je me mette à jour sur les différents copyrights. Disons que si vous utilisez une partie significative d'un document, ça parait normal que vous le citiez en référence l'auteur, le titre et le lien bringdadabeer.com Je n'ai pas l'intention de devenir riche en vendant mes oeuvres, c'est juste que si vous trouvez qu'un bout est suffisamment intéressant pour que vous vous en inspiriez largement, votre lecteur aura peut-être envie lui aussi d'aller voir le document en entier.

Amusez-vous bien !

Versions

  • initiale : 25 septembre
  • corrections typos : février

Les bases : adresses, pointeurs et indirection

Notion d'adresse

Vous comprenez certainement le programme suivant

Si ce n'est pas le cas, désolé, ce document n'est pas fait pour vous. Ne perdez pas votre temps, trouvez-vous un cours d'initiation à la programmation en C (ou mieux sur un langage avec lequel la programmation est plus facile à apprendre).

Vous savez donc que sert à lire des données, et à afficher leur valeur. Et on vous a dit qu'il fallait mettre pour les variables lues par (sauf si ce sont des chaînes), et ne pas en mettre dans la liste d'arguments de . C'est tout-à-fait vrai, on ne vous a pas menti.

Pour être plus précis, prend comme arguments une chaine de caractères (le format) et une liste de valeurs à afficher.

Du reste on aurait pu se passer de la variable et écrire directement :

parce qu'on fournirait directement la valeur calculée.

Pour ce qui est de , on ne lui donne évidemment pas en argument la valeur à lire, puisque cette valeur est fournie par l'utilisateur qui tape la réponse. Ce qu'on indique, c'est l'endroit où il faut la mettre. Pour cela, on transmet l'adresse de cet endroit.

Et la notation désigne exactement ceci : l'adresse de la zone mémoire qui correspond à la variable .

Parce que, comme vous le savez aussi, quand on déclare une variable dans une fonction, ça veut dire que quand on exécutera cette fonction, il y aura un petit bout de mémoire réservé pour cette variable.

Donc résumons :

  1. Vous avez déjà vu, et utilisé dans vos programmes, des adresses
  2. Une variable correspond à un emplacement dans la mémoire de l'ordinateur. Par exemple pour un , on aura une zone de 4 ou 8 octets, sur les machines actuelles. Un occupera un octet.
  3. L'opérateur (on peut l'appeler address-of) retourne l'adresse d'une variable.

Les adresses commes valeurs

Quand vous avez commencé à utiliser et , vous êtes peut-être arrivé à la conclusion qu'il fallait toujours mettre dans un , et jamais dans un .

C'était vrai pour ce que vous en faisiez, mais en fait, vous pouvez parfaitement écrire :

ce qui fait afficher l'adresse (pas la valeur) de la variable .

L'expression , si vous préférez, c'est la valeur de l'adresse. Une adresse, c'est une position en mémoire, c'est-à-dire un numéro d'octet (la mémoire est composée d'octets repérés par leur numéro).

Ici, comme on a utilisé la spécification de format (avec un comme pointeur), l'adresse (qui est un nombre) est affichée en hexadécimal, sous la forme "". On aurait pu l'afficher en décimal , mais en général, quand on s'intéresse à ce genre de choses, l'hexadécimal est plus pratique à utiliser.

Note : pour se conformer strictement à la norme du langage C, il faudrait écrire

pour transtyper explicitement l'adresse en "pointeur sur ".

Explication : c'est nécessaire parce que la conversion de type pointeur ne se fait pas automatiquement avec la fonction , parce qu'elle est variadique.

Conseil : ne vous inquiétez pas si vous ne comprenez rien au paragraphe précédent. C'est une formule magique qui me permet d'échapper aux malédictions des puristes, qui m'accuseraient de vous fourvoyer dans des "undefined behaviours". Point n'est mon intention. Vous verrez des transtypages (dont l'utilité vous apparaitra mieux) plus loin dans ce document.

Et les pointeurs ?

Définition : Un pointeur, c'est une variable (ou une donnée) qui contient une adresse. Ou plutôt, peut en contenir, parce que si le pointeur n'est pas initialisé, il contient n'importe quoi, qui n'est pas forcément une adresse valide.

On déclare un pointeur en précédant son nom par le symbole . Exemple

déclare que la variable est susceptible de contenir l'adresse d'un emplacement mémoire contenant lu-même un .

On peut modifier le programme précédent pour employer cette variable :

Ci-dessous une représentation des deux variables :

Les rectangles figurent les emplacements occupés par des variables, et leur valeur est marquée de dedans. Pour le pointeur, au lieu d'y mettre le nombre qui n'aura pas de grande signification pour nous, on dessine une flèche vers l'emplacement qu'il désigne (l'emplacement numéro , justement).

On dit alors que la variable pointe sur. Ça veut dire qu'elle contient son adresse.

A quoi vont me servir les pointeurs ?

Un premier usage, c'est pour écrire des fonctions qui modifient des données. Pour cela, ces fonctions reçoivent l'adresse de la donnée à modifier.

Par exemple, une fonction qui demande à taper un entier compris entre deux valeurs. Exemple d'utilisation :

C'est cohérent avec la manière d'utiliser : on fournit l'adresse de la variable , que la fonction est chargée de remplir avec une valeur demandée à l'utilisateur.

Dans la déclaration de la fonction, le paramètre correspondant est donc un pointeur d'entier :

Il ne reste plus qu'à écrire cette fonction. Une première version

L'étoile , opérateur d'indirection

Pendant qu'on y est, on peut se demander comment faire afficher la valeur de l'âge à partir du pointeur qui contient l'adresse de la variable.

C'est ce qu'on appelle faire une indirection. On ne veut pas faire afficher la valeur du pointeur, mais la valeur de la donnée dont le pointeur contient l'adresse.

Exercice : modifiez la fonction précédente pour qu'elle

  • vérifie que le nombre tapé est bien compris dans les valeurs limites,
  • repose la question tant que ce n'est pas le cas.

Indication : on mettra là où on aurait mis un entier.

Relation entre usage et déclaration de variable

Un principe fondamental de C (et C++) est que la déclaration d'une variable ressemble à la manière de l'employer.

Quand on compare la variable, on écrit quelque chose comme :

on y emploie qui est un . Donc elle est déclarée ainsi : . C'est simple, finalement, quand on connait le principe.

Si on avait un tableau de 10 adresses d'entiers, désignerait une adresse d'entiers, et un entier. Donc le tableau serait déclaré

A quoi ça sert de savoir ça ? parce qu'il y a un piège dans lequel les débutants tombent souvent. C'est de considérer que la déclaration

déclare comme un pointeur d'entiers, et que "donc" dans

serait aussi un pointeur d'entiers. Hé non, perdu. La lecture correcte, c'est que

  • et sont des
  • et donc est un pointeur d'entier
  • mais pas , qui est un .

Pour éviter les confusions, le plus sage est donc d'écrire en collant l'étoile du coté de la variable, voire de déclarer séparément.

En réalité le compilateur ne s'intéresse pas à savoir si c'est collé à gauche ou à droite, ça lui est complètement égal. Le nombre d'espaces n'est pas significatif. Si on dit ça, c'est pour faciliter la vie de celui qui doit relire le programme. Et l'heureux gagnant qui bénéficie de votre délicate attention, ça risque d'être vous un certain temps, tant que votre programme ne marche pas. Et aussi plus tard quand il faudra le modifier.

La constante

Un pointeur sert à contenir l'adresse d'une donnée. Parfois on a besoin de savoir que le pointeur ne contient pas encore (ou ne contient plus) une adresse valide.

Une constante spéciale, appelée sert à représenter "l'adresse de rien du tout". C'est utile par exemple pour initialiser un pointeur.

Bien entendu, les choses se passeront mal si on tente de "déréférencer" (faire une indirection avec) un pointeur . En général, le programme s'arrête avec une "violation mémoire".

Nous aurons l'usage de avec les structures de données chaînées.

Petits exercices

Échanger deux nombres (exercice guidé)

Vous connaissez la séquence pour échanger les valeurs contenues dans deux variables et :

Faites-en une fonction qui échange deux nombres, et qui aura comme paramètres les adresses de ces nombres :

Idée : dans la séquence montrée plus haut, remplacez simplement par , et pareil pour .

Pour tester la fonction, faites un qui déclare et affecte deux variables, les échange, et affiche leurs valeurs.

Ordonner deux variables

Le bout de programme suivant doit

  • lire deux entiers,
  • les ordonner pour les faire afficher dans l'ordre

Par exemple si on rentre 11 et 22, ou 22 et 11, ça écrira "".

A compléter

Suite : dans la fonction , utilisez - si ce n'est pas fait - la fonction .

Complément : pointeurs vers des structures, la notation "flèche"

Lorsqu'on utilise des pointeurs vers des structures, par exemple

on a souvent besoin d'accéder à un champ de la structure dont l'adresse est dans un pointeur.

Par exemple, pour normaliser une fraction, il faut d'abord ramener le dénominateur à une valeur positive. On peut écrire

La notation est simplement une abréviation commode de .

Pointeurs de fonctions

Vous en avez peut-être entendu parler comme quelque chose d'extraordinaire, relevant quasiment de la magie noire. En fait ça n'a rien de compliqué :

  • un pointeur d'entier, c'est un truc qui indique où se trouve la valeur d'un entier
  • un pointeur de fonction, ça indique où se trouve le code d'une fonction !

Ca sert à plein de choses :

  • au niveau de la programmation système, pour gérer les signaux (indiquer la fonction à lancer quand un signal sera reçu), pour la programmation parallèle (la fonction qu'un thread devra exécuter)
  • au niveau de la bibliothèque standard, la fonction "générique" permet de trier un tableau de données de n'importe quelle type, à condition de lui fournir une fonction qui permet de comparer,
  • pour écrire du code générique, utiliser des "callbacks" etc.

La logique

En fait c'est assez logique, en appliquant les principes déjà vus. Il n'y a pas grand chose de nouveau.

Voyons sur un exemple : une fonction qui applique une même fonction à tous les éléments d'un tableau.

  1. Pour afficher les éléments d'un tableau d'entiers, on peut faire comme ceci
  1. soyons sérieux, remplaçons l'action 1 par un appel de fonction. Pour cela on définit une fonction, appelée dans le maixn
  1. Imaginons que nous ayons réussi de mettre l'adresse de la fonction dans un pointeur de fonction. Il serait cohérent avec les épisodes précédents de remplacer par dans la boucle :
  1. Maintenant, rappelez-vous que la déclaration d'une variable ressemble à son utilisation. Puisque a pour but de remplacer qui a comme prototype

la variable peut être déclarée

  1. En pratique, dans un prototype de fonction, on indique les types des paramètres mais pas forcément leur nom. Et voila le code qui en résulte :

Note on peut aussi écrire

Dans le premier cas, c'est une histoire de compatibilité entre plusieurs compilateurs avant l'établissement d'un standard pour C. Du coup, ou , ce sont des expressions qui retournent une adresse de fonction, qu'on peut affecter dans un pointeur.

Dans le second cas, c'est que la forme d'un appel de fonction est plus générale que ce à quoi vous êtes habitué.

  • Avant de lire ce cours, vous connaissiez nom_de_fonction(parametre, parametre, )

  • et puis maintenant, pointeur_de_fonction(parametre, parametre, ),
  • et aussi pointeur_de_fonction(parametre, parametre, ).

En fait la forme générale, c'est expression_qui_retourne_une_fonction(parametre, parametre, )

Jusqu'ici on mettait "" et les "" dans les utilisations de pointeurs de fonctions parce que ça permet de bien en voir la logique. Maintenant que vous avez compris comment ça marche, on va laisser tomber ces opérateurs superflus.

Déclarer un type pour les fonctions ()

Pour jouer avec les fonctions, on a tout intérêt à déclarer un type. Exemple

pour les fonctions qui prennent un paramètre de type entier et répondent vrai ou faux.

Il y a deux raisons :

  1. d'abord la syntaxe pour décrire une fonction est un peu longue, avec le type de retour et ceux des paramètres, c'est fastidieux à relire autant qu'à écrire
  2. ensuite c'est parfois impossible à formuler directement, le compilateur ne voulant pas d'une déclaration bizarre au milieu d'une autre.

En reprenant l'exemple précédent, on peut écrire une fonction qui applique la même action à tous les éléments d'un tableau d'entiers. La ligne

définit le type : les fonctions qui prennent comme paramètre un et ne retournent rien. Et on utilise ce type pour un des paramètres de :

Le devient

Fonctions qui retournent des fonctions

On peut même jouer avec des fonctions qui retournent des fonctions.

mais bon, il faut en avoir l'usage.

Ce qu'on en retiendra, c'est que l'appel de fonction a une forme générale

expr_1arg_1arg_2

dans laquelle l'expr_1 peut être toute expression qui retourne un pointeur de fonction. Pas simplement le nom d'une fonction.

Une application : table d'actions

Ici on utilise une table d'actions, contenant des adresses de fonctions.

Les pointeurs et les tableaux

Qu'est-ce qu'un tableau ?

En C, vous avez peut être remarqué que les tableaux n'étaient pas des variables comme les autres. Par exemple vous pouvez déclarer deux tableaux de même type et de même taille :

mais vous ne pouvez pas copier l'un dans l'autre par ;

La raison, c'est que , ce n'est pas vraiment une variable contenant 10 entiers. C'est un emplacement où il y a 10 entiers.

Quand vous écrivez , vous demandez de mettre une adresse dans une autre. Ce n'est pas vraiment ce que vous voulez faire.

Compatibilité entre tableaux et pointeurs

Bref, il vous faut voir le tableau comme l'adresse du premier entier, d'une zone qui en contient Et logiquement vous avez le droit d'écrire :

ce qui met dans l'adresse de .

Inversement, vous pouvez aussi utiliser avec les pointeurs la notation indicée que vous connaissez sur les tableaux.

Bref, un tableau en C, c'est comme un pointeur (non modifiable) vers un espace qui a été réservé pendant la déclaration.

Exercice: que fait afficher la séquence suivante ?

Complément : Cette interchangeabilité explique pourquoi vous pouvez utiliser indifféremment les deux versions (pointeurs / tableaux) pour déclarer les fonctions qui ont des tableaux en paramètres

La notation pointeur+entier

Si on a un tableau d'entiers (par exemple), la notation désigne la position du k-ième entier après celui qui est désigné par

Illustration : le dessin ci-dessous représente la mémoire occupée par un tableau de trois entiers déclaré par

en supposant que chaque est représenté sur 4 octets (les petites cases), ce qui est le cas des machines à architecture 32 bits.

En haut on voit les positions désignées par , (à partir de , elles sont en dehors du tableau !), et en bas, les contenus correspondant à , ,

Bref, la notation ou a un sens en C, c'est la base de ce qu'on appelle l'arithmétique des pointeurs.

L'arithmétique des pointeurs

Exemple, les deux expressions

signifient toutes deux "mettre dans l'adresse qui est un plus loin que ce que désignait juste avant".

Autrement dit, si désignait une case d'un tableau, maintenant il désigne la case suivante.

Il faut comprendre un peu ce qui se passe en dessous : si un pointeur contient l'adresse d'un objet de type , et que est un entier, contient l'adresse de , augmentée de fois

Une démonstration ? Voici un code qui fait afficher la valeur d'un pointeur (de ), à qui on ajoute des entiers

Ce qu'on voit à l'exécution :

Commentaire : même si les adresses sont affichées en hexadécimal, il n'est pas trop difficile de voir qu'elles progressent de 8 en 8. La raison, c'est que ce sont des adresses de s, qui occupent chacun 8 octets sur cette machine.

Exercice : que pensez-vous de ce qui suit ?

Exercice : c'est quoi le problème avec ce code (qui plante) ?

(Conseil: activer les avertissements du compilateur).

Retour sur les expressions indicées

Avant de lire ce document, vous connaissiez évidemment la notation indicée sur les tableaux

et vous venez d'apprendre que ça marchait aussi avec les pointeurs

ce qui vous conduit à vous demander si il y a d'autres formes possibles. En fait oui et non, il y en a une seule pour les expressions indicées, qui est plus générale

expression_1expression_2

et qui permet de faire beaucoup de choses. Elle marche quand

  • l'expression 1 fournit une adresse
  • l'expression_2 fournit un entier.

Un exemple amusant (?) est celui de la conversion d'un nombre (de 0 à 15) en chiffre en hexadécimal

qui mettra un 'B' dans (je vous laisse y réfléchir).

Un autre exemple

ici l'expression_1 est un appel de fonction, qui retourne un pointeur (en fait un tableau de chaines).

Les chaines de caractères

En C, il n'existe pas de type spécifique pour les chaines de caractères. En fait une chaine, c'est une suite d'octets en mémoire, terminée par un caractère nul ().

Et pour désigner une chaine de caractères, on donne simplement l'adresse de son premier octet.

Le compilateur offre simplement quelques raccourcis. Une chaine notée entre guillemets représente un tableau de caractères.

Les deux notations sont équivalentes

elles réservent 6 octets pour le contenu de la chaine et le caractère nul qui sert de terminateur.

Mais pas tout-à-fait équivalentes à

qui réserve de la place

  • dans un espace mémoire pour
  • pour un pointeur contenant initialement l'adresse de cette chaine

Notez qu'on pourra faire mais pas : la place pour a été réservée dans un espace protégé en écriture. C'est une constante.

Bref, une chaine de caractère est décrite par l'adresse de son premier caractère, son contenu va de cette adresse jusqu'au premier caractère nul qui suit.

Traditionnellement, l'écriture de fonctions sur les chaines de caractères est souvent faite par le biais de pointeurs plutôt que d'indices.

Pour reprendre un exemple éculé, le calcul de la taille d'une chaine peut s'écrire ainsi (avec des indices) :

ou, avec un pointeur

mais plutôt sous la forme idiomatique (boucle ) du parcours d'une chaine

Dans tous les cas, l'opération "" fait glisser le pointeur vers le caractère suivant. L'expression indique la "distance" (nombre d'octets) entre le début de la chaine et le caractère nul qui la termine .

Un exemple avec tout ça

Objectif : gestion de stocks

Ci-dessous vous trouverez un début de programme dont le prétexte est la gestion d'un stock d'articles.

Chaque article est enregistré dans une structure qui regroupe une référence (un numéro), une description, et un nombre d'articles.

Le stock est enregistré dans un tableau d'articles, plus précisément dans une structure qui contient à la fois un tableau, et le nombre d'articles (les articles enregistrés seront en début de tableau).

Les fonctions qui agissent sur un Stock

Pour agir sur le stock, on a écrit un certain nombre de fonctions qui ont donc un " en paramètre. On passe un pointeur pour plusieurs raisons :

  • parce qu'on ne peut pas faire autrement pour les opérations qui ont pour but de modifier le stock. Avec un passage par valeur, la fonction modifierait une copie et pas le stock qu'on lui indique.
  • efficacité parce si on passe une par valeur, on en ferait une copie. Faites afficher pour voir combien d'octets seraient copiés à chaque appel. Attention, retourne un entier non signé (type ) pour lequel il faut utiliser la spécification de format (merci à Marc Mongenet de me l'avoir rappelé).
  • par homogénéité pour que tous les appels se ressemblent, et que le programmeur d'application ne doive pas faire d'effort de mémoire pour retrouver les noms.

C'est la même situation que quand vous utilisez des comme paramètres de diverses fonctions : , , , , ,

Le source

Travail proposé

  • Essayer de comprendre.
  • Compléter avec des fonctions qui permettent de consulter la quantité disponible pour une référence donnée, la modifier, etc.
  • Écrire un programme qui dialoguera avec l'utilisateur pour gérer un stock.
  • Faire une bibliothèque, compilée séparément.

Indications pour la compilation séparée

Si on part sur l'idée d'une compilation séparée, le fichier source contiendra un "include" de la bibliothèque, et les fonctions qui sont particulières au test

Le fichier "" contient les constantes, les déclarations de type et les prototypes des fonctions

Quant au fichier ", il fait une inclusion du fichier d'entête, et contient le code des fonctions

Pointeurs et typage

Les pointeurs sont typés

Jusqu'ici nous avons utilisé des pointeurs "sur des entiers", des pointeurs "sur des structures", etc.

Ces pointeurs sont typés, c'est-à-dire que le compilateur connait le type des objets pointés. Il y a deux raisons pour cela

  1. D'une part le compilateur vérifie qu'on ne se mélange pas les pinceaux en écrivant
  1. D'autre part le compilateur a besoin de connaitre la longueur du type pointé pour pouvoir traduire les expressions avec indices et l'arithmétique des pointeurs.

En effet, que fait , si p est un pointeur d'entiers () ?

Supposons que contienne l'adresse . C'est l'adresse (sur 64 bits, les zéros non significatifs ne sont pas indiqués) d'un qui occupe 32 bits, soit 4 octets. (Les chiffres sont donnés pour mon PC à architecture 64 bits, ils peuvent varier selon les machines, les sytemes et les compilateurs).

pointe "un plus loin" en mémoire, donc correspond à la valeur . L'opération consiste donc à ajouter 4 (le nombre d'octets d'un ) à l'adresse contenue dans .

En résumé avec la déclaration

T

l'instruction ajoute en réalité à l'adresse contenue dans .

Pointeurs génériques

Quand on programme "à bas niveau" (ce qui est le créneau spécifique du langage C), on a parfois besoin d'écrire des fonctions qui agissent sur des paramètres de différents types.

Par exemple, je voudrais voir comment sont codées les données, et pour cela faire afficher leur contenu en hexadécimal.

Exemple d'utilisation

Pour cela, je vais définir une fonction que j'appellerai ainsi

Le premier paramètre sera l'adresse du premier octet de la donner à afficher, le second sa longueur.

Remarquez que dans le premier cas l'argument donné est un , dans le second un . Il va y avoir un souci de compatibilité de types.

Ce n'est pas difficile si la zone est considérée comme un tableau d'octets (en C, octet = )

La spécification de format s'analyse comme suit

  • : afficher en hexadécimal
  • : en mettant éventuellement des 0 non significatifs
  • : sur une largeur de 2 caractères

Pointeurs génériques : le type

Pour déclarer la fonction, nous allons utiliser le type spécial , qui signifie "pointeur sur un type inconnu".

Ce type est compatible, pour l'affectation, avec les autres pointeurs. C'est ce qu'on appelle un pointeur générique.

  1. d'une part ça permet l'appel de la fonction avec n'importe quel type d'adresse :

  2. d'autre part ça autorise l'affectation dans le corps de la fonction.

  1. Par contre un pointeur générique ne peut évidemment pas être déréférencé, et on ne peut pas faire d'arithmétique des pointeurs avec, puis qu'on ne connait ni le type, ni la longueur de ce qu'il pointe.

Conversions explicites de pointeurs (transtypage, typecast)

Une autre façon de faire "à l'ancienne" aurait été de définir la fonction avec un paramètre "pointeur d'octet" :

ce qui interdit l'appel

parce que les types et sont incompatibles.

Dans ce cas, la solution est de forcer la conversion de type ("typecast")

en précédant l'adresse par "" qui signifie "considéré comme pointeur de ".

Vous verrez ça dans de nombreux exemples que vous trouverez sur Internet. Il faut savoir que nombreuses bibliothèques (allocation dynamique, réseau, ) ont été écrites à une époque où le type n'avait pas encore été introduit en C (C89). La pratique normale était alors d'utiliser des , ce qui imposait d'avoir à faire un "transtypage" explicite.

Nous sommes au XXIe siècle, Les bibliothèques ont été modifiées depuis, mais les exemples préhistoriques sont restés.

Résumé : si expr est une expression de type "pointeur sur type T", alors

T) expr

est de type "pointeur sur T".

L'allocation dynamique

Preliminaires, durée de vie des variables

Considérons un programme simple, avec une variable globale

On y voit plusieurs catégories de variables

  • la variable qui est globale,
  • les paramètres et , qui sont des noms pour les valeurs reçues lors d'un appel
  • les variables locales et

et vous savez qu'elles n'ont pas la même visibilité :

  • la variable globale est accessible depuis toutes les fonctions
  • alors que les variables locales (et les paramètres) n'existent que dans leur fonction (le même nom peut être utilisé dans plusieurs fonctions) ;

et surtout, pas la même durée de vie :

  • la variable globale existe pendant toute la durée d'exécution du programme : sa place a été réservée au début de l'exécution. C'est ce qu'on appelle une variable statique.
  • une variable locale, n'existe que pendant l'exécution de la fonction : de l'espace est réservé automatiquement (sur la pile des appels) pour loger la variable quand la fonction est appelé, et rendu automatiquement au retour de la fonction (exécution de , ou de la dernière instruction). C'est ce qu'on appelle une variable automatique.

Le dessin ci-dessous représente l'évolution (le temps va de gauche à droite) du contenu de la mémoire au cours de appels et des retours. En haut figure la pile d'exécution, avec dans chaque contexte les variables et leur valeur. En bas, la variable globale.


  • Advanced rocketry mod minecraft 1.12.2
  • Kodi sur smart tv samsung
  • Spider-man far from home film vf ubtobox
  • Kitchensims 3
  • Plus longlet dans firefox
  • La chtite famille sur uptobox
  • Mes donnees sanpchat
  • Musique adobe reader