Lorsque l'on souhaite associer plusieurs valeurs de types différents, on a recours à un enregistrement. Le type de cet enregistrement, dit type structuré ou plus simplement structure, est défini ainsi :
struct employe { unsigned id; char* nom; float salaire; };
Une fois le type défini, on peut créer des variables pour stocker des enregistrements :
int main(void) { struct employe paul = {0, "Paul", 1203.45f}; struct employe jacques = {1, "Jacques", 1587.33f}; ... }
La plupart des opérateurs ne fonctionnent pas sur les enregistrements, mais il reste quand même =, ==, != et &. De plus, il existe un opérateur dédié qui permet d'accéder à chaque champ individuellement :
paul.salaire += 50.0f;
Et puisqu'il arrive régulièrement que l'on manipule un enregistrement par son adresse, une variante de cet opérateur est adaptée à ce cas :
struct employe* employe_du_mois = &jacques; ... printf(" Félicitations %s\n", employe_du_mois->nom);
Remarque Dans le cas (fréquent) où un enregistrement occupe plus de 64 bits en mémoire, il est préférable de le passer par adresse plutôt que par valeur à une fonction. Si aucune modification de l'enregistrement n'est nécessaire durant l'exécution de la fonction, le paramètre correspondant est déclaré avec le qualificatif const.
Pour améliorer la lisibilité du code, il arrive fréquemment que l'on souhaite renommer un type. Par exemple :
typedef unsigned long couleur;
Ceci permet de distinguer les valeurs qui servent à représenter une couleur des autres valeurs de type unsigned long.
Dans le cas des structures, cette technique nous donne l'occasion d'éviter de répéter le mot-clé struct sans cesse.
struct etudiant_s { char nom[50]; char prenoms[50]; int age; }; typedef struct etudiant_s etudiant; int main(void) { etudiant e = {"Martin", "Jean-Louis Benjamin", 19}; ... }
Date. Écrivez un programme qui affiche la date au format ISO 8601 (AAAA-MM-JJ).
La fonction time vous donne cette date, mais exprimée en secondes écoulées depuis le 1er janvier 1970, ce qui n'est pas très pratique. La fonction localtime permet de décomposer la date en éléments exploitables.
Tailles. Écrivez un programme qui affiche la taille en octets d'un enregistrement contenant trois champs de type char. Êtes-vous surpris ?
Modifiez le programme pour que le deuxième champ soit un int. Comment expliquez-vous la nouvelle taille ? Placez ce champ en premier dans la définition de la structure. Qu'est-ce qui change ?
Numéro. Dans un système d'exploitation POSIX (comme ici ArchLinux), les utilisateurs sont représentés par des numéros nommés UID. Écrivez un programme qui affiche votre UID en vous aidant de la fonction getpwnam (lisez sa page manuelle).
Complexes. Un complexe est un nombre pouvant s'écrire en forme algébrique z = x + i.y où i désigne la racine carrée de -1.
on nomme x la partie réelle de z et y sa partie imaginaire. Définissez une structure pour représenter un complexe sous forme algébrique.
Écrivez ensuite des fonctions pour calculer le module, le conjugué et l'inverse d'un complexe. Pour mémoire :
Rectangles. Un rectangle est représenté par les coordonnées entières de son coin supérieur gauche et par ses dimensions entières (largeur et hauteur).
Définissez la structure correspondante. En vous basant sur ce type, écrivez une fonction qui affiche un rectangle, et une fonction qui demande à l'utilisateur de cliquez deux fois et qui renvoie le rectangle ayant un coin à chaque position cliquée.
Alterations. Reprenez l'exercice précédent. Ajoutez une procédure qui effectue une translation sur un rectangle et une procédure qui effectue la rotation de 90° ou -90° centrée sur son coin supérieur gauche.
Combinaisons. Reprenez l'exercice précédent. Ajoutez une fonction qui renvoie l'intersection de deux rectangles, et une fonction qui renvoie la réunion de deux rectangle (c'est à dire le plus petit rectangle qui les contient tous les deux).