La norme C est désormais ISO/IEC 9989:2011
La norme C 2011 a été publiée le lundi 2011-12-19 par l'ISO (ou, plus précisément, la notification de sa publication a été ajoutée au site web du comité le 19 ; la norme peut avoir été publiée depuis 2011-12-08). Voir l'annonce sur le site WG14 site web. Malheureusement, le PDF à partir d'ISO coûte 338 CHF, et de ANSI 387 USD .
- Vous pouvez obtenir le PDF de la norme INCITS/ISO/IEC 9899:2012 (C2011) à l'adresse suivante ANSI pour 30 USD.
- Vous pouvez obtenir le PDF de la norme INCITS/ISO/IEC 14882:2012 (C++2011) à l'adresse suivante ANSI pour 30 USD.
Réponse principale
La question est la suivante : "Les typographies répétées sont-elles autorisées en C ? La réponse est "Non - pas dans les normes ISO/IEC 9899:1999 ou 9899:1990". La raison est probablement historique : les compilateurs C originaux ne le permettaient pas, et les normalisateurs originaux (qui étaient mandatés pour normaliser ce qui était déjà disponible dans les compilateurs C) ont normalisé ce comportement.
Voir le réponse par Als pour les cas où la norme C99 proscrit les typedefs répétés. La norme C11 a modifié la règle du §6.7 ¶3 comme suit :
3 Si un identificateur n'a pas de lien, il ne doit pas y avoir plus d'une déclaration de l'identificateur (dans un déclarateur ou un spécificateur de type) avec la même portée et dans le même espace de nom, sauf que que :
- un nom de typedef peut être redéfini pour désigner le même type que celui qu'il désigne actuellement, à condition que ce type ne soit pas un type variablement modifié ;
- peuvent être redéclarées comme indiqué au point 6.7.2.3.
Il y a donc maintenant un mandat explicite pour un typedef répété dans C11. Il faut attendre la disponibilité de compilateurs C conformes à la norme C11.
Pour ceux qui utilisent encore C99 ou une version antérieure, la question de suivi est alors, vraisemblablement, "Alors comment éviter de rencontrer des problèmes avec les typedefs répétés ?".
Si vous suivez la règle selon laquelle il n'y a qu'un seul en-tête qui définit chaque type qui est nécessaire dans plus d'un fichier source (mais il peut y avoir de nombreux en-têtes définissant ces types ; chaque type distinct ne se trouve cependant que dans un seul en-tête), et si cet en-tête est utilisé à chaque fois que ce type est nécessaire, alors vous ne rencontrez pas de conflit.
Vous pouvez également utiliser des déclarations de structure incomplètes si vous n'avez besoin que de pointeurs vers les types et n'avez pas besoin d'allouer la structure réelle ou d'accéder à ses membres (types opaques). Encore une fois, définissez des règles concernant l'en-tête qui déclare le type incomplet, et utilisez cet en-tête partout où le type est nécessaire.
Voir aussi Que sont les variables externes en C ; elle parle de variables, mais les types peuvent être traités de manière assez similaire.
Question des commentaires
J'ai vraiment besoin des "déclarations de structure incomplètes", en raison de complications distinctes du préprocesseur qui interdisent certaines inclusions. Donc vous dites que je ne dois pas typer ces déclarations avant si elles sont typées à nouveau par l'en-tête complet ?
Plus ou moins. Je n'ai pas vraiment eu à m'occuper de cela (bien que certaines parties des systèmes au travail soient très proches de ce problème), donc c'est un peu provisoire, mais je pense que cela devrait fonctionner.
En général, un en-tête décrit les services externes fournis par une "bibliothèque" (un ou plusieurs fichiers source) de manière suffisamment détaillée pour que les utilisateurs de la bibliothèque puissent compiler avec elle. En particulier dans le cas où il y a plusieurs fichiers source, il peut également y avoir un en-tête interne qui définit, par exemple, les types complets.
Tous les en-têtes sont (a) autonomes et (b) idempotents. Cela signifie que vous pouvez (a) inclure l'en-tête et tous les autres en-têtes requis sont automatiquement inclus, et (b) vous pouvez inclure l'en-tête plusieurs fois sans encourir la colère du compilateur. Ce dernier point est généralement obtenu grâce à des gardes d'en-tête, bien que certains préfèrent l'option #pragma once
- mais ce n'est pas portable.
Ainsi, vous pouvez avoir un en-tête public comme celui-ci :
public.h
#ifndef PUBLIC_H_INCLUDED
#define PUBLIC_H_INCLUDED
#include <stddef.h> // size_t
typedef struct mine mine;
typedef struct that that;
extern size_t polymath(const mine *x, const that *y, int z);
#endif /* PUBLIC_H_INCLUDED */
Jusqu'ici, pas de grande controverse (bien que l'on puisse légitimement soupçonner que l'interface fournie par cette bibliothèque est très incomplète).
private.h
#ifndef PRIVATE_H_INCLUDED
#define PRIVATE_H_INCLUDED
#include "public.h" // Get forward definitions for mine and that types
struct mine { ... };
struct that { ... };
extern mine *m_constructor(int i);
...
#endif /* PRIVATE_H_INCLUDED */
Encore une fois, ce n'est pas très controversé. Le site public.h
L'en-tête doit être listé en premier ; cela permet de vérifier automatiquement l'auto-contrôle.
Code du consommateur
Tout code qui nécessite le polymath()
services écrit :
#include "public.h"
Ce sont toutes les informations nécessaires pour utiliser le service.
Code du fournisseur
Tout code de la bibliothèque qui définit le polymath()
services écrit :
#include "private.h"
Par la suite, tout fonctionne normalement.
Autre code de fournisseur
S'il y a une autre bibliothèque (appelez-la multimath()
) qui utilise le polymath()
services, alors ce code doit inclure public.h
comme n'importe quel autre consommateur. Si le polymath()
font partie de l'interface externe de multimath()
alors le multimath.h
L'en-tête public comprendra public.h
(désolé, j'ai changé de terminologie vers la fin, ici). Si le multimath()
services dissimulent complètement le polymath()
services, alors le multimath.h
L'en-tête ne comprendra pas public.h
mais le multimath()
ou les fichiers sources individuels qui ont besoin de l'en-tête privé peuvent le faire. polymath()
peuvent l'inclure si nécessaire.
Tant que vous suivez religieusement la discipline consistant à inclure l'en-tête correct partout, vous ne rencontrerez pas de problème de double définition.
Si vous constatez par la suite qu'un de vos en-têtes contient deux groupes de définitions, l'un qui peut être utilisé sans conflit et l'autre qui peut parfois (ou toujours) entrer en conflit avec un nouvel en-tête (et les services qui y sont déclarés), vous devez alors diviser l'en-tête original en deux sous-en-têtes. Chaque sous-en-tête suit individuellement les règles élaborées ici. L'en-tête original devient trivial - une garde d'en-tête et des lignes pour inclure les deux fichiers individuels. Tout le code de travail existant reste inchangé - bien que les dépendances changent (fichiers supplémentaires à dépendre). Le nouveau code peut maintenant inclure le sous-en-tête acceptable pertinent tout en utilisant le nouvel en-tête qui entre en conflit avec l'en-tête original.
Bien sûr, vous pouvez avoir deux en-têtes qui sont tout simplement inconciliables. Pour un exemple artificiel, s'il existe un en-tête (mal conçu) qui déclare une version différente de l'en-tête FILE
(à partir de la version dans <stdio.h>
), vous êtes fichu ; le code peut inclure soit l'en-tête mal conçu, soit l'option <stdio.h>
mais pas les deux. Dans ce cas, l'en-tête mal conçu devrait être révisé pour utiliser un nouveau nom (peut-être File
mais peut-être quelque chose d'autre). Vous pourriez rencontrer ce problème de manière plus réaliste si vous deviez fusionner le code de deux produits en un seul après un rachat d'entreprise, avec certaines structures de données communes, telles que DB_Connection
pour une connexion à une base de données. En l'absence de l'option C++ namespace
vous vous retrouvez avec un exercice de renommage pour un ou deux lots de code.