Je suis un étudiant en première année d'informatique et mon professeur a dit #define
est interdit dans les normes de l'industrie avec #if
, #ifdef
, #else
, et quelques autres directives de préprocesseur. Il a utilisé le mot "interdit" en raison d'un comportement inattendu.
Est-ce exact? Si oui, pourquoi?
Existe-t-il en fait des normes interdisant l'utilisation de ces directives?
D'abord j'en ai entendu parler.
Non; #define
et ainsi de suite sont largement utilisés. Parfois trop utilisé, mais définitivement utilisé. Il y a des endroits où la norme C impose l'utilisation de macros - vous ne pouvez pas les éviter facilement. Par exemple, §7.5 Erreurs <errno.h>
dit:
Les macros sont
EDOM EILSEQ ERANGE
qui se développent en expressions constantes entières avec le type
int
, des valeurs positives distinctes et qui peuvent être utilisées dans#if
directives de prétraitement; …
Compte tenu de cela, il est clair que toutes les normes de l'industrie n'interdisent pas l'utilisation des directives de macro du préprocesseur C. Cependant, il existe des normes de `` meilleures pratiques '' ou de `` directives de codage '' de diverses organisations qui prescrivent des limites à l'utilisation du préprocesseur C, bien qu'aucune n'interdise complètement son utilisation - c'est une partie innée de C et ne peut être totalement évitée. Souvent, ces normes sont destinées aux personnes travaillant dans des domaines critiques pour la sécurité.
Une norme, vous pouvez vérifier la norme MISRA C (2012); qui a tendance à proscrire les choses, mais même cela reconnaît que #define
et al sont parfois nécessaires (section 8.20, les règles 20.1 à 20.14 couvrent le préprocesseur C).
Le NASA GSFC (Goddard Space Flight Center) C Coding Standards dit simplement:
Les macros ne doivent être utilisées qu'en cas de besoin. La surutilisation des macros peut rendre le code plus difficile à lire et à maintenir car le code ne se lit plus ou ne se comporte plus comme le standard C.
La discussion après cette déclaration d'introduction illustre l'utilisation acceptable des macros de fonction.
CERT C Coding Standard a un certain nombre de directives sur l'utilisation du préprocesseur et implique que vous devez minimiser l'utilisation du préprocesseur, mais n'interdit pas son utilisation.
Stroustrup voudrait rendre le préprocesseur non pertinent en C++, mais cela ne s'est pas encore produit. Comme Peternotes , certaines normes C++, telles que les Normes de codage JSF AV C++ ( Joint Strike Fighter, Air Vehicle) à partir de 2005 environ, impose une utilisation minimale du préprocesseur C. Essentiellement, les règles JSF AV C++ le limitent à #include
et le #ifndef XYZ_H
/#define XYZ_H
/…/#endif
danse qui empêche les inclusions multiples d'un seul en-tête. C++ a quelques options qui ne sont pas disponibles en C - notamment, une meilleure prise en charge des constantes typées qui peuvent ensuite être utilisées dans des endroits où C ne permet pas leur utilisation. Voir également static const
contre #define
vs enum
pour une discussion des problèmes.
C'est une bonne idée de minimiser l'utilisation du préprocesseur - il est souvent abusé au moins autant qu'il est utilisé (voir Boostpréprocesseur 'bibliothèque' pour des illustrations de jusqu'où vous pouvez aller avec le préprocesseur C).
Le préprocesseur fait partie intégrante de C et #define
et #if
etc ne peut être totalement évité. La déclaration du professeur dans la question n'est généralement pas valable: #define
est interdit dans les normes de l'industrie avec #if
, #ifdef
, #else
, et quelques autres macros est au mieux une sur-déclaration, mais peut être prise en charge avec une référence explicite à des normes spécifiques de l'industrie (mais les normes en question n'incluent pas ISO/IEC 9899: 2011 - le C la norme).
Notez que David Hammen a fourni des informations sur une norme de codage C spécifique - la Norme de codage C JPL - qui interdit beaucoup de choses que beaucoup de gens utilisation en C, notamment en limitant l'utilisation du préprocesseur C (et en limitant l'utilisation de l'allocation dynamique de mémoire et en interdisant la récursivité - lisez-le pour voir pourquoi et décidez si ces raisons vous concernent).
Non, l'utilisation des macros n'est pas interdite.
En fait, l'utilisation de #include
les gardes dans les fichiers d'en-tête sont une technique courante qui est souvent obligatoire et encouragée par les directives de codage acceptées. Certaines personnes affirment que #pragma once
est une alternative à cela, mais le problème est que #pragma once
- par définition, puisque les pragmas sont un crochet fourni par la norme pour les extensions spécifiques au compilateur - n'est pas standard, même s'il est pris en charge par un certain nombre de compilateurs.
Cela dit, il existe un certain nombre de directives de l'industrie et de pratiques encouragées qui découragent activement toute utilisation de macros autres que #include
gardes en raison des problèmes que les macros introduisent (non respect de la portée, etc.). Dans le développement C++, l'utilisation des macros est encore plus mal vue que dans le développement C.
Décourager l'utilisation de quelque chose n'est pas la même chose que l'interdire, car il est toujours possible de l'utiliser légitimement - par exemple, en documentant une justification.
Certaines normes de codage peuvent décourager ou même interdire l'utilisation de #define
Pour créer des macros de type fonction qui acceptent des arguments, comme
#define SQR(x) ((x)*(x))
car a) de telles macros ne sont pas de type sécurisé, et b) quelqu'un inévitablement écrit SQR(x++)
, ce qui est mauvais juju.
Certaines normes peuvent décourager ou interdire l'utilisation de #ifdef
Pour la compilation conditionnelle. Par exemple, le code suivant utilise une compilation conditionnelle pour imprimer correctement une valeur size_t
. Pour C99 et versions ultérieures, vous utilisez le spécificateur de conversion %zu
; pour C89 et versions antérieures, vous utilisez %lu
et convertissez la valeur en unsigned long
:
#if __STDC_VERSION__ >= 199901L
# define SIZE_T_CAST
# define SIZE_T_FMT "%zu"
#else
# define SIZE_T_CAST (unsigned long)
# define SIZE_T_FMT "%lu"
#endif
...
printf( "sizeof foo = " SIZE_T_FMT "\n", SIZE_T_CAST sizeof foo );
Certaines normes peuvent exiger qu'au lieu de cela, vous implémentiez le module deux fois, une fois pour C89 et plus tôt, une fois pour C99 et plus tard:
/* C89 version */
printf( "sizeof foo = %lu\n", (unsigned long) sizeof foo );
/* C99 version */
printf( "sizeof foo = %zu\n", sizeof foo );
puis laissez Make (ou Ant , ou quel que soit l'outil de construction que vous utilisez) gérer la compilation et la liaison de la version correcte. Pour cet exemple, ce serait une exagération ridicule, mais j'ai vu du code qui était un nid de rat introuvable de #ifdef
S que devrait avoir avait ce code conditionnel pris en compte dans des fichiers séparés.
Cependant, je ne suis au courant d'aucune entreprise ou groupe industriel qui a interdit l'utilisation des déclarations de préprocesseur.
Les macros ne peuvent pas être "interdites". La déclaration est absurde. Littéralement.
Par exemple, section 7.5 Erreurs <errno.h>
du C Standard nécessite l'utilisation de macros:
1 L'en-tête
<errno.h>
définit plusieurs macros, toutes relatives au signalement des conditions d'erreur.2 Les macros sont
EDOM EILSEQ ERANGE
qui se développent en expressions constantes entières avec le type
int
, des valeurs positives distinctes et qui peuvent être utilisées dans#if
directives de prétraitement; eterrno
qui se développe en une valeur l modifiable qui a le type
int
et la durée de stockage local du thread, dont la valeur est définie sur un numéro d'erreur positif par plusieurs fonctions de bibliothèque. Si une définition de macro est supprimée pour accéder à un objet réel ou si un programme définit un identifiant avec le nomerrno
, le comportement n'est pas défini.
Ainsi, non seulement les macros sont une partie obligatoire de C, dans certains cas, leur non-utilisation entraîne un comportement indéfini.
Non, #define
N'est pas interdit. Une mauvaise utilisation de #define
, Cependant, peut être mal vue.
Par exemple, vous pouvez utiliser
#define DEBUG
dans votre code afin que plus tard, vous puissiez désigner des parties de votre code pour la compilation conditionnelle en utilisant #ifdef DEBUG
, à des fins de débogage uniquement. Je ne pense pas que quelqu'un de sensé voudrait interdire quelque chose comme ça. Les macros définies à l'aide de #define
Sont également largement utilisées dans les programmes portables, pour activer/désactiver la compilation de code spécifique à la plate-forme.
Cependant, si vous utilisez quelque chose comme
#define PI 3.141592653589793
votre enseignant peut à juste titre souligner qu'il est préférable de déclarer PI
comme une constante avec le type approprié, par exemple,
const double PI = 3.141592653589793;
car il permet au compilateur de vérifier le type lorsque PI
est utilisé.
De même (comme mentionné par John Bode ci-dessus), l'utilisation de macros de type fonction peut être refusée, en particulier en C++ où des modèles peuvent être utilisés. Donc au lieu de
#define SQ(X) ((X)*(X))
envisager d'utiliser
double SQ(double X) { return X * X; }
ou, en C++, mieux encore,
template <typename T>T SQ(T X) { return X * X; }
Encore une fois, l'idée est qu'en utilisant les fonctionnalités du langage au lieu du préprocesseur, vous autorisez le compilateur à taper check et aussi (éventuellement) à générer un meilleur code.
Une fois que vous aurez suffisamment d'expérience en codage, vous saurez exactement quand il convient d'utiliser #define
. Jusque-là, je pense que c'est une bonne idée pour votre professeur d'imposer certaines règles et normes de codage, mais de préférence, ils devraient eux-mêmes connaître et pouvoir expliquer les raisons. Une interdiction générale de #define
Est absurde.
C'est complètement faux, les macros sont fortement utilisées en C. Les débutants les utilisent souvent mal mais ce n'est pas une raison pour les interdire de l'industrie. Une mauvaise utilisation classique est #define succesor(n) n + 1
. Si vous vous attendez à ce que 2 * successor(9)
donne 20, alors vous vous trompez car cette expression sera traduite par 2 * 9 + 1
c'est-à-dire 19 et non 20. Utilisez des parenthèses pour obtenir le résultat attendu.
Non, ce n'est pas interdit. Et à vrai dire, il est impossible de faire du code multiplateforme non trivial sans lui.
Contrairement à toutes les réponses à ce jour, l'utilisation des directives de préprocesseur est souvent interdite dans le calcul haute fiabilité. Il existe deux exceptions à cette règle, dont l'utilisation est obligatoire dans ces organisations. Voici les #include
directive, et l'utilisation d'un include guard dans un fichier d'en-tête. Ces types d'interdictions sont plus probables en C++ plutôt qu'en C.
Voici un exemple: 16.1.1 Utilisez le préprocesseur uniquement pour implémenter les gardes include et inclure les fichiers d'en-tête avec les gardes include.
Autre exemple, cette fois pour C plutôt que C++: Norme de codage institutionnelle JPL pour le langage de programmation C. Cette norme de codage C ne va pas jusqu'à interdire complètement l'utilisation du préprocesseur, mais elle se rapproche. Plus précisément, il dit
Règle 20 (utilisation du préprocesseur) L'utilisation du préprocesseur C doit être limitée à l'inclusion de fichiers et aux macros simples. [Règle 8 du pouvoir des dix].
Je ne tolère ni ne décrie ces normes. Mais dire qu'ils n'existent pas est ridicule.
Les macros sont assez utilisées dans GNU land C, et sans commandes de préprocesseur conditionnel, il n'y a aucun moyen de gérer correctement plusieurs inclusions des mêmes fichiers source, de sorte que cela me semble être des fonctionnalités de langage essentielles pour moi .
Peut-être que votre classe est en fait sur C++, qui, malgré l'échec de nombreuses personnes, doit être distinguée de C car c'est un langage différent, et je ne peux pas parler de macros là-bas. Ou peut-être que le professeur voulait dire qu'il les interdisait dans sa classe. Quoi qu'il en soit, je suis sûr que la communauté SO serait intéressée à entendre de quelle norme il parle, car je suis presque sûr que toutes les normes C prennent en charge l'utilisation de macros.
Si vous souhaitez que votre code C interagisse avec le code C++, vous devrez déclarer vos symboles visibles de l'extérieur, tels que les déclarations de fonctions, dans le extern "C"
espace de noms. Cela se fait souvent en utilisant la compilation conditionnelle:
#ifdef __cplusplus
extern "C" {
#endif
/* C header file body */
#ifdef __cplusplus
}
#endif
Regardez n'importe quel fichier d'en-tête et vous verrez quelque chose comme ceci:
#ifndef _FILE_NAME_H
#define _FILE_NAME_H
//Exported functions, strucs, define, ect. go here
#endif /*_FILE_NAME_H */
Ces définitions sont non seulement autorisées, mais de nature critique car chaque fois que le fichier d'en-tête est référencé dans des fichiers, il sera inclus séparément. Cela signifie que sans définir, vous redéfinissez tout ce qui se trouve entre les gardes plusieurs fois, ce qui ne se compile pas dans le meilleur des cas et dans le pire des cas, vous vous expliquerez plus tard pourquoi votre code ne fonctionne pas comme vous le souhaitez.
Le compilateur utilisera également define as seen ici avec gcc qui vous permet de tester des choses comme la version du compilateur qui est très utile. Je travaille actuellement sur un projet qui doit être compilé avec avr-gcc, mais nous avons un environnement de test avec lequel nous exécutons également notre code. Pour empêcher les fichiers et registres spécifiques avr de garder notre code de test en cours d'exécution, nous faisons quelque chose comme ceci:
#ifdef __AVR__
//avr specific code here
#endif
En utilisant cela dans le code de production, le code de test complémentaire peut être compilé sans utiliser avr-gcc et le code ci-dessus est uniquement compilé en utilisant avr-gcc.
Si vous venez de mentionner #define
, J'aurais pensé qu'il faisait peut-être allusion à son utilisation pour les énumérations, qu'il vaut mieux utiliser enum
pour éviter des erreurs stupides telles que l'attribution de deux fois la même valeur numérique.
Notez que même pour cette situation, il est parfois préférable d'utiliser #define
s que les énumérations, par exemple si vous comptez sur des valeurs numériques échangées avec d'autres systèmes et que les valeurs réelles doivent rester les mêmes même si vous ajoutez/supprimez des constantes (pour des raisons de compatibilité).
Cependant, en ajoutant que #if
, #ifdef
, etc. ne doit pas être utilisé non plus, c'est juste bizarre. Bien sûr, ils ne devraient probablement pas être maltraités, mais dans la vraie vie, il existe des dizaines de raisons de les utiliser.
Ce qu'il a pu signifier pourrait être que (le cas échéant), vous ne devriez pas coder en dur le comportement dans la source (ce qui nécessiterait une recompilation pour obtenir un comportement différent), mais plutôt utiliser une certaine forme de configuration d'exécution à la place .
C'est la seule interprétation à laquelle je pouvais penser qui aurait du sens.