Concepts en C

Cohérence, logique et fonctionnement du C


précédentsommairesuivant

V. Grandeurs de type adresse

On a introduit, en décrivant les caractéristiques d'un objet, un type particulier de valeur qui représente l'adresse d'un objet. L'importance de ce type vient de ce que l'adresse permet d'accéder à l'objet.

Une seule constante de ce type, NULL, est définie et permet de représenter "l'adresse d'aucun objet".

On ne confondra pas le type adresse avec un type entier : la grandeur représentée est d'une nature différente et le sens des opérateurs est différent.

V-A. Type adresse


La notion d'adresse est plus riche que le simple repérage d'une position mémoire : le type de l'objet intervient dans la définition d'un type "adresse". Les raisons de ceci sont les suivantes :

  • Ceci permet de savoir quel objet est concerné par cette adresse. En effet, si un objet se situe à une et une seule position en mémoire, l'inverse n'est pas vrai : à une position mémoire donnée, on peut trouver plusieurs objets de type différent : c'est le cas si l'objet est un aggrégat (tableau ou structure) ou une union. Par exemple, si on a un tableau, l'adresse du tableau repère la même position mémoire que l'adresse du premier élément du tableau. Ce sont deux objets de type différent situés à une même position en mémoire et leur adresse sera en C d'un type différent.

  • Ceci permet de connaître le type du résultat de l'évaluation d'une expression comportant des adresses (par exemple *Ta est de type T)

  • Certains opérateurs sur les adresses utilisent implicitement la taille de l'objet adressé, donc doivent connaître son type.

  • L'exception à ce principe concerne le type void *.


Déclaration d'un type "adresse"

Le type Ta "adresse d'un objet de type T " sera noté en C en cohérence avec la signification de l'opérateur unaire * :

On écrira que l'objet à l'adresse de type Ta, (*Ta), est de type T :   T  (*Ta)    ou plus simplement :

T  * Ta
  • Si on préfère associer le signe * au type T,   T * Ta , on lira "Ta est un type adresse de T".

  • Si on préfère voir le signe * associé au type Ta,   T * Ta,  on lira "l'objet à l'adresse de type Ta est de type T".

On retrouvera ce souci de cohérence entre déclarations de type et action des opérateurs dans toutes les déclarations en C ce qui explique et justifie le caractère parfois ésotérique de la déclaration des types.
Voir à ce sujet les déclarateurs complexes

V-B. Opérateurs sur le type adresse

Le type "adresse d'objet" a son jeu propre d'opérateurs ayant un sens bien particulier. Certains de ces opérateurs binaires (à deux opérandes) ont la particularité d'associer deux valeurs de types radicalement différents (adresse et entier), ce qui est une exception en C, pour fournir une valeur de type adresse.

V-B-1. Les opérateurs unaires sizeof, * et ->

Ces opérateurs ont été déjà décrits et on se reportera au chapitre III-A

V-B-2. Les opérateurs + et - entre une adresse et un entier

Ces opérateurs sont utilisés lorsqu'on a un ensemble d'objets d'un même type T placés consécutivement en mémoire (autrement dit un tableau d'objets T).

Si A est l'adresse d'un objet de type T (donc A est de type T*) et n un entier :

  • A+n permet d'obtenir l'adresse du nieme objet de type T qui serait situé après (si n>0) ou avant (si n <0) l'objet d'adresse A

  • A-n permet d'obtenir l'adresse du nième objet de type T qui serait situé avant (si n>0) ou après (si n <0) l'objet d'adresse A

Ces opérateurs ont besoin de connaître la taille de l'objet adressé, donc son type.

V-B-3. L'opérateur []

Cet opérateur est dérivé de l'opérateur unaire * et de l'opérateur + sur les adresses et permet une écriture et une lecture plus naturelle. Cet opérateur a donc besoin de connaître la taille de l'objet adressé, donc son type.

L'un des deux opérandes doit être une adresse A et l'autre un entier n.

A[n] ou n[A], écriture peu courante, sont tous deux équivalents à *(A+n)

V-B-4. L'opérateur - entre deux adresses

L'opérateur - est également défini entre deux adresses A et B d'un même type T* :

Si B == A+n , alors n == B-A


Cet opérateur a besoin de connaître la taille de l'objet adressé, donc son type.

L'opérateur + entre adresses n'est pas défini.
Ainsi, si a, b et c sont du type T*, il est illégal d'écrire a+b-c (équivalent à (a+b)-c)
mais correct d'écrire a+(b-c) qui est une expression du type T*

V-B-5. Les opérateurs de comparaison

Ce sont les opérateurs ==, !=, <=, <, > , >=

- Les deux premiers permettent de savoir si les adresses sont celles d'un même objet ou de deux objets différents.

- Les quatre derniers permettent de connaître la position relative en mémoire de deux objets. Cette information n'est pertinente que si ces objets appartiennent à une même structure de données ( tableau ou struct).

V-C. Type void *

Le type "adresse d'objet" spécifie normalement le type de l'objet adressé. Il est quelquefois utile que cette information soit, au moins provisoirement, non précisée. Dans ce cas, le type adresse sera "adresse d'un type void" : void  *.

Naturellement, comme le compilateur ne connait pas le type réel de l'objet, on ne peut utiliser l'opérateur unaire *, les opérateurs + , - portant sur les adresses ni l'opérateur []. On ne peut finalement pas faire grand chose avec une adresse de ce type, à part utiliser les opérateurs de comparaison, et il faudra, pour pouvoir utiliser les autres opérateurs, rétablir par un transtypage explicite le type de l'adresse en accord avec le véritable type de l'objet adressé (et surtout ne pas se tromper !).

On a avec le type void  * un type d'adresse s'appliquant à tous les objets. On l'appelle quelquefois "type générique d'adresse" .
A noter que le type void  ** n'est pas, lui, un type générique d'adresse : on connaît parfaitement le type de l'objet adressé (void  *)

A noter que le terme void  * est assez maladroitement choisi puisqu'il ne s'agit pas du tout d'une adresse de "rien" (une adresse de "rien" est représentée par la valeur NULL), mais d'une adresse (valide) d'objet dont le type est non précisé ou est la valeur NULL.

V-D. Objets pointeurs

Les objets permettant de stocker ce type d'information sont appelés pointeurs.
Un pointeur est donc simplement un objet qui contient comme information une adresse d'objet de type T. Son type (et celui de sa valeur) est donc T*.

Outre les opérateurs s'appliquant aux adresses, les opérateurs suivants sont définis pour les objets pointeurs : & ++ -- ainsi que les opérateurs d'assignation =, +=, etc. Voir le chapitre III-A-2.

  • On ne peut donner, de façon portable, à un pointeur une valeur entière à l'exception de 0, valeur qui se transtype automatiquement en la valeur NULL.
    On préférera toujours assigner NULL à un pointeur plutot que 0

  • On exprime quelquefois, pour simplifier, le type "adresse d'un objet de type T" comme le type "pointeur sur T" et la valeur de l'adresse d'un objet de type T comme une "constante pointeur sur T" (on fera la différence avec un objet pointeur déclaré const : " pointeur constant sur T").

précédentsommairesuivant

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

Ce document est issu de http://www.developpez.com et reste la propriété exclusive de son auteur. La copie, modification et/ou distribution par quelque moyen que ce soit est soumise à l'obtention préalable de l'autorisation de l'auteur.