Une union est un type défini possédant plusieurs champs, comme une structure. La différence est qu'un seul champ peut contenir des données à la fois. La taille en mémoire d'un tel type est donc seulement la taille de son plus grand champ.
union variant { int entier; float reel; }; ... union variant v; v.entier = 12; v.reel = 5.7f; printf("%d ?!?\n", v.entier);
Le plus souvent, cette construction est employée au sein d'une structure pour permettre à différents enregistrements de contenir différentes sortes de données.
Dans une structure, un champ de bits est un champ dont on limite volontairement le nombre de bits significatifs. Plusieurs champs de bits peuvent ainsi se partager les bits d'un même entier et donc prendre moins de place.
typedef struct { unsigned int rouge : 5; unsigned int vert : 6; unsigned int bleu : 5; } couleur16;
Un champ de bits doit obligatoirement être un int, mais il peut être qualifié de unsigned, signed, const ou volatile. Contrairement à un champ normal, un champ de bits n'est pas compatible avec les opérateurs & et sizeof.
En C il existe 4 classes de mémorisation pour les variables. La classe auto représente les variables locales que nous avons utilisé jusqu'ici. On peut utiliser ce mot comme qualificatif lors de la déclaration d'une variable, mais il est sous-entendu par défaut au sein des fonctions.
La classe register représente les variables qu'il est souhaitable de ne pas garder en mémoire mais directement dans un registre du processeur. Elles se comportent comme les variables auto en terme de portée. Il est rare que l'on utilise cette classe car les compilateurs peuvent déjà décider efficacement quelles données il est rentable de conserver en registre.
La classe extern représente les variables de portée globale, c'est à dire qu'elles sont disponibles depuis n'importe où dans le programme (même d'un fichier à un autre), et elles existent durant toute l'exécution du programme. Une variable déclarée en dehors d'une fonction appartient à cette classe par défaut. Si elle n'est pas initialisée, une variable extern vaut 0 au début de l'exécution du programme.
La classe static est à mi-chemin des précédentes. Une telle variable existe durant toute l'exécution du programme, et commence à 0 si elle n'est pas autrement initialisée. Par contre elle n'est visible que dans un contexte local : la fonction où elle est déclarée (le cas échéant), ou le fichier où elle est déclarée sinon.
Les fonctions sont stockées en mémoire et ont donc une adresse, comme les variables. On peut placer l'adresse d'une fonction dans un pointeur, la transmettre en tant que paramètre, et enfin invoquer la fonction à travers son adresse plutôt que son nom.
typedef double (*fonction)(double); double zero(double min, double max, double prec, fonction f) { double moy; while (fabs(max-min) > prec) { moy = (max + min)/2.0; if (f(min)*f(moy) > 0) min = moy; else max = moy; } return min; } double parabole(double x) { return 2-x*x; } ... printf("%f\n", zero(0.0, 4.0, 0.0001, parabole);
Remarque Une adresse de fonction ne supporte pas les opérations d'addition et de soustraction disponibles pour les adresses de variables.
Nous savons déjà utiliser le préprocesseur pour définir des constantes, mais il est capable de créer des pseudo-fonctions basées sur le même mécanisme de remplacement de texte.
#define MULT(a, b) a*b ... double x=1.0, y=2.5; int n = 12, m = -1; printf("%f\n", MULT x, y)); printf("%d\n", MULT(n, m)); printf("%d\n", MULT(n+m, 2));
Notez que les paramètres des macros sont traités en tant que texte et peuvent donc être de n'importe quel type. Par contre, les paramètres ne sont pas évalués avant d'être passés à la pseudo-fonction et cela peut donner des résultats surprenants (comme dans l'exemple ci-dessus).
Dans le corps d'une macro possédant un paramètre a, la notation #a représente le texte de a placé entre guillemets et est donc considéré comme une constante chaîne de caractères. Pour utiliser cette notation à son plein potentiel, il est utile de se souvenir que plusieurs chaînes littérales placées d'affilée dans un code source seront automatiquement concaténées par le préprocesseur.
Nous avons déjà rencontré des fonctions dont le nombre d'arguments d'appels est variable : printf, scanf, etc. Pour définir une telle fonction, il faut utiliser les types et macros de l'en-tête stdarg.h.
#include <stdarg.h> double somme_de_double(int nb, ...) { double res = 0.0; va_list arguments; va_start(arguments, nb); while (nb-->0) res += va_arg(arguments, double); va_end(arguments); return res; }
Au moins un paramètre de la fonction doit être fixé car il sert de repère pour la macro va_start. Les paramètres anonymes sont placés après les paramètres fixés.
Les arguments correspondant aux paramètres anonymes peuvent être de n'importe quel type, mais ils seront convertis avant d'être passés à la fonction par un mécanisme nommé promotion par défaut. char et short sont promus en int, tandis que float est promu en double.
Soldats. Un jeu de stratégie fait s'affronter différents types d'unités. Ces types se distinguent sur plusieurs caractéristiques :
Ce jeu étant destiné à des plateformes mobiles, ces données doivent occuper le moins de place possible. Écrivez donc à l'aide de champs de bits une définition de structure pour les types d'unités. Écrivez également une fonction qui crée un type d'unité dont toutes les caractéristiques valent 7.
Faites ensuite une deuxième version de votre programme qui n'utilise pas les champs de bits mais sans prendre plus de place en mémoire.
Clubs. On souhaite constituer pour un vendeur d'équipements de golf un catalogue de clubs. Pour chaque article, on retient la marque, le modèle, la catégorie et la sous-catégorie.
Catégorie | Sous-catégorie |
---|---|
Bois | un entier entre 1 et 9 |
Fer | un entier entre 1 et 9 |
Wedge | Pitching, Gap, Sand, Lob ou Flip |
Putter | Regular, Belly ou Long |
Définissez d'abord deux énumérations pour les sous-catégories des Wedges et des Putters, puis une structure pour représenter un article.
Écrivez une fonction qui affiche tous les articles contenus dans le catalogue (un tableau d'articles).
Dans un catalogue de taille réelle, un affichage complet prendrait bien trop de place. On souhaite donc permettre de filtrer l'affichage. Une fonction de filtre est une fonction qui prend en argument un article et renvoie un booléen qui est vrai si et seulement si l'article a passé le filtre. Définissez le type adresse de fonction de filtre, puis modifiez la fonction d'affichage pour qu'elle n'affiche que les articles filtrés. Testez-la avec un filtre qui laisse seulement passer les fers longs (fer 1 à fer 4).
Accolades. Écrivez une fonction qui prend en argument un tableau d'entiers courts non signés ainsi que sa taille, et qui l'affiche au format :
{1, 2, 3, 4}
Vous ne devrez employer ni boucles, ni fonctions intermédiaires.
Colonnes. On dispose d'un répertoire composé de contacts. Chaque contact possède un nom et un numéro de téléphone.
Définissez un type structuré pour représenter un contact. Le répertoire sera simplement un tableau d'enregistrements.
Écrivez ensuite une macro paramétrée qui prend en argument une chaîne de caractères et un entier naturel, puis qui affiche cette chaîne, tronquée ou complétée par des espaces pour atteindre un nombre de caractères égal à l'entier fourni.
Écrivez enfin une fonction qui affiche un contact (à l'aide de la macro) et un programme qui affiche tout un répertoire à l'aide de cette fonction.
Sauriez-vous remplacer la macro paramétrée par une fonction normale ?
Sortie. Écrivez une fonction qui fait le même travail que printf. Pour simplifier la tâche, on permettra d'utiliser seulement %c comme conversion (sans indicateur).