-2
votes

Travailler de la compilation conditionnelle #if et #else (et autres) en C

J'ai essayé d'écrire un programme à l'aide de certaines directives de pré-traitement de la compilation conditionnelle au lieu de "if-else" comme suit.

#include<stdio.h>
int main ()
{
    int x;
    scanf ("%d",&x);
#if (x==5)
    printf ("x is 5");
#else
    printf ("x not 5");
#endif
}


2 commentaires

Le préprocesseur ne peut pas utiliser de variables du programme C.


Le préprocesseur fonctionne pendant la compilation. Pour vérifier les valeurs d'exécution, utilisez normalement si .


4 Réponses :


-1
votes

C'est le "pré-processeur". "PRE" signifie "avant".

Vous essayez d'utiliser une valeur runtime lors du prétraitement! Le préprocesseur bien sûr n'a pas accès à ces informations lors de la construction.

Ce problème ne se limite pas aux valeurs d'exécution, mais est plus fondamentale. Même si vous essayiez d'utiliser une constante de temps de compilation (nommée) telle que consexpr int x = 2 , vous ne pouvez pas le faire. Ce sont deux langues entrelacées, comme la génération de HTML avec PHP; Le HTML n'a aucune connaissance des variables PHP et le PHP n'a aucune connaissance de ce que Widgets clique sur la page. Ce sont des contextes d'exécution complètement différents sans interaction intégrée ni compatibilité croisée.


11 commentaires

Curieux de savoir les bowvotes, personnellement. Je pensais que c'était une analogie décente.


En distinguant le "pré-processeur", cette réponse nous dit qu'il y a une activité de préprocesseur et qu'il existe une activité de processeur et que l'activité de préprocesseur arrive avant l'activité du processeur, mais cela ne nous dit pas ce qui est dans l'activité de préprocesseur et de ce qui est dans l'activité du processeur. Donc, il ne dit pas à l'op que x == 5 ne peut être évalué que pendant l'activité du processeur si x n'est pas un identifiant de pré-dépannage. Il ne distingue même pas ce qui est un identifiant de préprocesseur d'autres types d'identifiants.


En outre, il est incorrect que "même si vous essayiez d'utiliser une constante de temps de compilée", cela ne fonctionnerait pas, car 2 + 2 + 1 est une constante de compilation mais peut également être utilisée. dans les expressions de préprocesseur.


@ERICPOSTPISCHIL Une constante de temps de compilation telle que consexpr int x = 2 , qui ne peut pas avoir de composant d'exécution mais ne peut néanmoins être complètement invisible au préprocesseur. Alors, changons-le à "Nommé Compil-Time Constant".


@Ericpostpischil et je n'ai pas reçu votre premier commentaire, alors peut-être que vous pourriez y réécrire? Non, je ne suis pas allé dans une exploration académique des types d'identificateurs - je ne trouve pas que ce mandat -2 cependant.


C n'a pas consexpr . C permet à une expression constante dans dans #if , sauf que, après l'expansion macro et le traitement de défini , tous les identificateurs restants sont remplacés par 0.


En ce qui concerne mon premier commentaire, vous avez dit à OP que quelque chose se passe avant autre chose, mais vous n'avez pas dit ce que chaque quelque chose est. Étant donné que la sémantique était inconnue de OP, utilisons des termes que vous ne connaissez pas la signification de l'illustration, disons "Glurmf" et "Zugmar". OP n'était pas au courant que int x définit un x connu uniquement à la ZUGMAR, mais que #if est traité par le glurmf. Votre réponse ne dit simplement pas qu'un identifiant x déclaré avec int x sera traité comme zéro pendant le glurmf. Dire à l'OP que Glurmf est avant que Zugmar ne les informent pas de cela.


@ERICPOSTPISCHIL Il ne dit pas cela parce que cela déforme la situation. L'identifiant x déclaré avec int x n'a rien à voir avec le x trouvé dans le glumf, car il est évalué dans un contexte totalement différent. C'est le point du problème, et c'est ce que je [essayé de] expliquer. :) Quoi qu'il en soit, nous pouvons accepter de ne pas être d'accord. Merci pour votre contribution.


Re "L'identifiant X déclaré avec int x n'a rien à voir avec le X trouvé dans le glumf, car il est évalué dans un contexte totalement différent.": La réponse ne dit pas cela. Les informations sont simplement manquantes de la réponse.


@Ericpostpischil Oui, il fait - par analogie avec PHP et HTML qui existent dans exactement la même circonstance. Comme je l'ai dit, nous pouvons simplement accepter de ne pas être d'accord maintenant. Passe une bonne soirée.


Supposons que je n'avais jamais vu c avant mais je connaissais php et html. Quel texte dans votre réponse me dit si int x est dans l'analogique PHP ou dans l'analogique HTML?



1
votes

Le prétraitement a lieu avant la compilation. Donc, le préprocesseur ne sait rien sur votre code C ou vos variables. Vous ne pouvez utiliser aucune variables C dans des conditions.

La compilation conditionnelle est à des fins différentes. xxx


8 commentaires

Si le prétraitement a lieu avant la compilation, pourquoi compiler les deux lignes void A [3]; et #Error avec clang produisent un message d'erreur pour la première ligne (tableau a Type d'élément incomplet) Avant un message d'erreur pour la deuxième ligne?


@ERICPOSTPISCHIL: Il y a la comme si règle. Le compilateur est autorisé à faire les choses différemment et n'a pas besoin de gérer toute la phase de préprocesseur avant de faire une analyse sémantique. La norme n'interdit pas ce que vous dites que vous attendez; Cela n'interdit pas non plus ce que vous signalez.


@Jonathanleffler: Je n'ai pas demandé pourquoi la standard C permet de cela. J'ai demandé comment la déclaration de P__j __ est cohérente avec le comportement de Clang. La spécification de la norme C est compatible avec le comportement de Clang à cet égard. Affirmer que le prétraitement a lieu avant que la compilation ne soit pas compatible avec le comportement de Clang.


@ERICPOSTPISCHIL CLANG n'est pas conforme dans ce cas: La mise en œuvre ne doit pas traduire avec succès une unité de traduction de prétraitement contenant une directive de prétraitement #Error, à moins que cela ne fait partie d'un groupe ignoré par inclusion conditionnelle.


@ EricpostPischil standard dans 5.1.1.2 phases de traduction montre que le prétraitement a lieu avant la compilation. Étape 7: Les jetons résultants sont analysées syntaxiquement et sémantiquement et traduites comme une unité de traduction.


@P__J____: 5.1.1.2 Spécifie la PRECÉDENCE des phases de traduction, pas l'ordre chronologique: "La priorité entre les règles de la syntaxe de traduction est spécifiée par les phases suivantes". Il est incorrect et trompeur d'indiquer que le prétraitement a lieu avant la compilation. Cette fausse déclaration pourrait induire en erreur une personne qui voit un message sur une erreur de phase 7 pour conclure que le prétraitement a terminé avec succès, et donc il n'y a donc pas d'erreurs de phase 1 à 6.


@EricpostPischil Clang n'est pas conforme dans ce cas La mise en œuvre ne doit pas traduire avec succès une unité de traduction de prétraitement contenant une directive de prétraitement #Error, à moins que cela ne fait partie d'un groupe ignoré par inclusion conditionnelle.


@P__J__: il se conformait car l'unité de traduction n'est pas traduite avec succès.



3
votes

x n'est pas une expression littérale entière ou une expression littérale entière (littéraux entier + opérateurs) ou une macro expansion à ceux-ci, donc dans un préprocesseur qui le remplace avec 0 ( 6.10.1P4 ). 0 == 5 est faux, donc la branche #else est prise.

Le préprocesseur ne connaît pas les déclarations, les types et tels. Cela ne fonctionne que avec des jetons (et des macros qui se développent finalement à ceux).


6.10.1P4

Après tous les remplaçants dues à la macro expansion et à la définie unaire opérateur ont été effectués, tous les identificateurs restants (y compris Les personnes lexiquement identiques aux mots-clés) sont remplacées par le numéro PP 0, puis chaque jeton de prétraitement est converti en jeton.


2 commentaires

@LightnessRacsinorbit J'ai ajouté une citation de la norme pour supporter cette réclamation. ;-) (Vous me devez maintenant un uppote: p).


Til! Je me suis dirigé vers Coliru pour rassembler des preuves incriminantes contre vous et laissé sous le choc: p merci pour la réf.



0
votes

opérandes dans #if Les instructions peuvent être uniquement des constantes, les choses définies avec #define et un opérateur spécial . Tous les autres identifiants de l'expression sont remplacés par 0. Le x dans votre code exemple n'est pas défini avec #define , donc (x == 5) devient (0 == 0) .

Dans la norme C 2018, la clause 6.10.1 nous indique que l'évaluation de l'expression dans un relevé #if comprend:

  • Les macros de préprocesseur (les choses définies avec #define ) sont remplacées en fonction de leurs définitions.
  • Utilisations de l'opérateur défini est remplacé par 0 ou 1.
  • Tous les identificateurs restants sont remplacés par 0.

    Parce que le x dans votre code exemple n'est pas défini avec #define , il est remplacé par 0 dans le #if . Cela se traduit en (0 == 5) , qui est faux, ce code entre le code entre #if et le #else est ignoré.

    Dans une instruction PreProcesseur, vous ne pouvez pas évaluer les variables en fonction des valeurs qui seront définies lors de l'exécution du programme.


0 commentaires