web-dev-qa-db-fra.com

Y a-t-il une bonne raison de toujours mettre un define entre parenthèses en C?

De toute évidence, il y a des moments où #define les instructions doivent avoir des parenthèses, comme ceci:

#define WIDTH 80+20

int a = WIDTH * 2; // expect a==200 but a==120

Donc, je garde toujours entre parenthèses, même quand ce n'est qu'un seul chiffre:

#define WIDTH (100)

Un nouveau venu sur C m'a demandé pourquoi je fais cela, j'ai donc essayé de trouver un cas Edge où l'absence de parenthèses sur un seul numéro #define cause des problèmes, mais je n'y pense pas.

Un tel cas existe-t-il?

48
weston

Oui. L'opérateur de concaténation du préprocesseur (##) Provoquera des problèmes, par exemple:

#define _add_penguin(a) penguin ## a
#define add_penguin(a) _add_penguin(a)

#define WIDTH (100)
#define HEIGHT 200    

add_penguin(HEIGHT) // expands to penguin200
add_penguin(WIDTH)  // error, cannot concatenate penguin and (100) 

Idem pour la stringisation (#). Il s'agit clairement d'un cas d'angle et n'a probablement pas d'importance compte tenu de la façon dont WIDTH sera vraisemblablement utilisé. Pourtant, c'est quelque chose à garder à l'esprit sur le préprocesseur.

(La raison pour laquelle l'ajout du deuxième pingouin échoue est un détail subtil des règles de prétraitement dans C99 - iirc il échoue car il concatène à deux jetons de prétraitement sans espace réservé doit toujours aboutir à un seul jeton de prétraitement - mais cela n'a aucune importance, même si la concaténation était autorisée, elle donnerait toujours un résultat différent de celui du #define!.

Toutes les autres réponses ne sont correctes que dans la mesure où cela n'a pas d'importance du point de vue du scanner C++ car, en effet, un nombre est atomique. Cependant, à ma lecture de la question, il n'y a aucun signe que seuls les cas sans extension supplémentaire du préprocesseur doivent être pris en compte, donc les autres réponses sont, même si je suis totalement d'accord avec les conseils qui y sont contenus, faux.

38
Alexander Gessler

Parfois, vous devez écrire du code non pas avec les mises en garde actuelles, mais avec les mises en garde de la prochaine fois que ça va être édité.

À l'heure actuelle, votre macro est un entier unique. Imaginez quelqu'un le modifier à l'avenir. Disons qu'ils ne sont pas vous, mais quelqu'un qui est moins prudent ou plus pressé. La parenthèse est là pour leur rappeler de mettre toute modification entre parenthèses.

Ce genre de réflexion est une bonne habitude en C. J'écris personnellement du code dans un style que certaines personnes pourraient trouver "redondant", avec des choses comme ça mais surtout en ce qui concerne la gestion des erreurs. La redondance concerne la maintenabilité et la composabilité des futures modifications.

26
asveikau

Comme Blagovest Buyukliev l'a dit:

La définition consiste en un seul jeton (un seul opérande, aucun opérateur), les parenthèses ne sont pas nécessaires car un seul jeton (tel que 100) est indivisible atom lors de la lexation et de l'analyse).

Mais je recommanderais les règles suivantes en ce qui concerne les macros:

  1. Évitez les fonctions comme les macros @Voir le commentaire de Lundin.

Si vous souhaitez utiliser des fonctions comme les macros, tenez compte des 2 règles suivantes:

  1. Utilisez toujours des crochets pour les arguments dans les macros
  2. N'utilisez un argument de macro qu'une seule fois

Pourquoi la règle 1.? (Pour garder l'ordre des opérations correct)

#define quad(x) (x*x)
int a = quad(2+3);

s'étendra à:

int a = (2+3*2+3);

Pourquoi la règle 2.? (Pour garantir qu'un effet secondaire n'est appliqué qu'une seule fois)

#define quad(x) (x*x)
int i = 1;
int a = quad(i++);

s'étendra à:

int a = i++ * i++;
8
Nicoretti

Chaque fois que la définition consiste en un seul jeton (un seul opérande, aucun opérateur), les parenthèses ne sont pas nécessaires car un seul jeton (tel que 100) est un indivisible atom lors du lexing et de l'analyse.

8
Blagovest Buyukliev

Puisque 100 est un seul jeton, je doute que vous trouverez un cas d'angle où les parenthèses comptent (pour un seul jeton!)

C'est toujours une bonne habitude OMI, car ils peuvent avoir de l'importance lorsqu'il y a plusieurs jetons impliqués.

6
NPE

Non. Il n'y a aucun cas où #define WIDTH 100 peut donner une expansion non ambiguë ou "surprenante". En effet, cela ne peut entraîner le remplacement d'un seul jeton par un seul jeton.

Comme vous le savez, une confusion de macro se produit lorsqu'un seul jeton (par exemple WIDTH) entraîne plusieurs jetons (par exemple 80 + 20). Pour autant que je puisse supposer, c'est la cause seulement pour l'utilisation de parenthèses dans les substitutions et, comme exploré dans mon premier paragraphe, cela ne s'applique pas ici.

Cependant, ce fait technique mis à part, il peut encore s'agir d'une bonne pratique. Il favorise l'habitude et sert également de rappel si cette macro est modifiée à quelque chose de plus complexe.

6

Lorsque le code ne définit qu'un nombre, @ Alexander Gessler répond bien à la question.

Pourtant, de nombreux codeurs ne remarquent pas les opérateurs unaires dans les cas suivants:

#define TEMPERATURE1M (-1)
#define TEMPERATURE1P (+1)

Lorsque le code utilise un #define qui emploie un opérateur, contenant () assure les résultats numériques attendus et la priorité.

#define TEMPERATURE_WITH  (-1)
#define TEMPERATURE_WITHOUT -1

// Consider how these will compile
int w  = 10-TEMPERATURE_WITH;
int wo = 10-TEMPERATURE_WITHOUT;  // May not compile

La dernière ligne de code peut compiler étant donné les changements sémantiques C99 @ Olaf

4
chux

Il y a parfois une bonne raison.

Pour un seul numéro, il n'y a pas de bonne raison.

Pour d'autres cas, comme vous l'avez montré, il y a une bonne raison.

Certaines personnes préfèrent faire très attention et toujours utiliser les parenthèses (@aix le recommande. Je ne le fais pas, mais il n'y a pas de réponse difficile).

3
ugoren

Cela ne fera certainement pas de mal et c'est une bonne habitude. Mais il n'y a pas de différence entre (100) et 100 pour les calculs numériques.

2
Jack Edmonds