précédent  index  suivant

6. Structures, unions, énumérations


6.1 Quelle est la différence entre struct et typedef struct ?

	struct x1 { ... };
	typedef struct { ... } x2;
		

La première écriture déclare un tag de structure x1. La deuxième déclare un nouveau type nommé x2.

La principale différence est l'utilisation. La deuxième écriture permet un peu plus l'abstraction de type. Cela permet de cacher le véritable type derrière x2, l'utilisateur n'étant pas sensé savoir que c'est une structure.

	struct x1 v1;
	x2 v2;
		

6.2 Une structure peut-elle contenir un pointeur sur elle-même ?

Oui.

Voir aussi la question 5.2.

6.3 Comment implémenter des types cachés (abstraits) en C ?

L'une des bonnes façons est d'utiliser des pointeurs sur des structures qui ne sont pas publiquement définies. On peut en plus cacher les pointeurs par des typedef.

Voir aussi la question 5.10.

6.4 Peut-on passer des structures en paramètre de fonctions ?

Oui, c'est parfaitement autorisé. Toutefois, rappelons que les paramètres en C sont passés par valeurs et copiés dans la pile. Pour des grosses structures, il est préférable de passer un pointeur dessus, et éventuellement un pointeur constant.

6.5 Comment comparer deux structures ?

Il n'existe pas en C d'opérateur ou de fonction pour comparer deux structures. Il faut donc le faire à la main, champs par champs.

Une comparaison bit à bit n'est pas portable, et risque de ne pas marcher, en raison du padding (alignement sur certains octets).

6.6 Comment lire/écrire des structures dans des fichiers ?

Il faut utiliser les fonctions fread() et fwrite(). Attention : les fichiers obtenus ne sont pas portables.

Une méthode plus portable consiste à enregistrer les structures dans un fichier texte.

6.7 Peut-on initialiser une union ?

La norme prévoit d'initialiser le premier membre d'une union. Pour le reste, ce n'est pas standard. En C99, on peut initialiser les champs d'une union :

	union { /* ... */ } u = { .any_member = 42 };
		

6.8 Quelle est la différence entre une énumération et des #define ?

Il y a peu de différences.

L'un des avantages de l'énumération est que les valeurs numériques sont assignées automatiquement. De plus, une énumération se manipule comme un type de données. Certains programmeurs reprochent aux énumérations de réduire le contrôle qu'ils ont sur la taille des variables de type énumération.

6.9 Comment récupérer le nombre d'éléments d'une énumération ?

Ceci n'est possible de façon automatique que si les valeurs se suivent.

	typedef enum { A, B, C, D} type_e;
		

Dans cette énumération, les valeurs sont données par le compilateur dans l'ordre croissant, à partir de 0 et avec un pas de 1. Ainsi, le nombre d'éléments de type_e est D + 1.

On peut rajouter un élément à l'énumération qui donne directement le nombre d'éléments :

	typedef enum { RED, BLUE, GREEN, YELLOW, NB_COLOR} color_e;
		

le nombre d'éléments de color_e est donc NB_COLOR.

Si l'on est obligé, pour des raisons diverses et variées, de fixer d'autres valeurs aux constantes, alors cette solution ne marche pas. On peut toujours rajouter un champs dans l'énumération et fixer manuellement sa valeur.

6.10 Comment imprimer les valeurs symboliques d'une énumération ?

On ne peut pas le faire simplement. Il faut écrire une fonction qui le fait. Un problème qui se pose alors est la maintenance, car une modification des valeurs de l'énumération entraîne la nécessité d'une mise à jour de cette fonction.

Voici un code qui limite les problèmes de mise à jour :

	/* Fichier foo.itm */
	ITEM(FOO_A)
	ITEM(FOO_B)
	ITEM(FOO_C)
	/**/

	/* Fichier foo.h */
	#ifndef FOO_H
	#define FOO_H
	#define ITEM(a) a,
	typedef enum {
	#include "foo.itm"
		FOO_NB
	} foo_t;
	#undef ITEM

	#define ITEM(a) #a,
	const char * const aFoo[] = {
	#include "foo.itm"
	};
	#undef ITEM
	#endif
	/**/

	/* Fichier foo.c */
	#include <stdio.h>
	#include "foo.h"
	int main(void) {
		foo_t foo;
		for (foo = FOO_A; foo < FOO_NB; foo++) {
			printf("foo=%d ('%s')\n",foo, aFoo[foo]);
		}

		return 0;
	}
		

Merci à Emmanuel Delahaye pour cet exemple.


précédent  index  suivant