Étranges définitions des macros VRAI et FAUX

300

J'ai vu les définitions de macro suivantes dans un livre de codage.

#define TRUE  '/'/'/'
#define FALSE '-'-'-'

Il n'y avait aucune explication.

S'il vous plaît me expliquer comment ceux - ci fonctionnent comme TRUEet FALSE.

Keshava GN
la source
63
Je pense que c'est juste une façon amusante de définir VRAI comme 1 et FAUX comme 0
BlackDwarf
160
Notez que c'est une idée terrible sans crochets autour de ces expressions. Je veux dire que c'est une idée terrible avec eux, mais sans vous demandez juste une longue nuit de débogage.
TartanLlama
70
Puis-je connaître le livre de codage auquel vous faites référence?
artm
47
J'espère que ce livre l'a inclus comme exemple de code mauvais ou délibérément obscur.
Jon Hanna du
31
@Daniel: Une autre idée serait que rand ()% 2 définisse MAYBE comme rand ()% 2, c'est-à-dire parfois == VRAI et parfois == FAUX.
Kaiserludi

Réponses:

380

Voyons voir: '/' / '/'signifie le charlittéral /, divisé par le charlittéral '/'lui-même. Le résultat est un, ce qui semble raisonnable TRUE.

Et '-' - '-'signifie le charlittéral '-', soustrait de lui-même. C'est zéro ( FALSE).

Il y a deux problèmes avec cela: d'abord, ce n'est pas lisible. Utilisation 1et 0est absolument mieux. De plus, comme TartanLlama et KerrekSB l'ont souligné, si vous prévoyez d'utiliser cette définition, veuillez ajouter des parenthèses autour d'eux afin de ne pas avoir de surprise:

#include <stdio.h>

#define TRUE  '/'/'/'
#define FALSE '-'-'-'

int main() {
        printf ("%d\n", 2 * FALSE);
        return 0;
}

Cela affichera la valeur du charlittéral '-'(45 sur mon système).

Avec des parenthèses:

#define TRUE  ('/'/'/')
#define FALSE ('-'-'-')

le programme imprime correctement zéro, même si cela n'a pas beaucoup de sens de multiplier une valeur de vérité par un entier, mais ce n'est qu'un exemple du genre de bogues inattendus qui pourraient vous mordre si vous ne mettez pas vos macros entre parenthèses.

Geai
la source
6
Merde, j'ai pris beaucoup de temps pour le comprendre: je pensais même que c'était une chose étrange en tant que glyphes ... je ne sais pas xD
Luis Masuelli
8
Il est en fait logique de multiplier par la valeur de vérité. Par exemple, l'indentation * should_indent entraînera soit 0 soit une indentation selon que should_indent sans branchement. (Je suppose que c'est un mauvais exemple, lorsque travailler avec une seule branche de texte n'a pas d'importance (j'ai vu cette technique dans les shaders et dans XPATH (tous deux trop différents et je ne me souviens pas de la forme exacte))
Alpedar
2
Alpedar - mais cela n'a pas de sens conceptuel et matériel de le faire - dans ce cas, il serait plus clair (et conceptuellement logique) d'utiliser un ifau lieu de le multiplier TRUEpar un entier.
Jay
4
Grande explication. Ayez un badge en or!
Michael Hampton
2
La négation logique peut être implémentée au fur notx = TRUE- x;et à mesure . Sauf que TRUE-FALSEc'est -44 (en supposant ASCII)
Hagen von Eitzen
89

C'est juste une autre façon d'écrire

#define TRUE 1
#define FALSE 0

L'expression '/'/'/'divisera la valeur char de '/'elle-même, ce qui donnera 1 comme résultat.

L'expression '-'-'-'va soustraire la valeur char de '-'d'elle-même, ce qui donnera 0 comme résultat.

Les crochets autour des defineexpressions entières manquent cependant, ce qui peut entraîner des erreurs dans le code à l'aide de ces macros. La réponse de Jay répond assez bien à cela.

Un exemple de scénario "réel" où l'oubli des crochets peut être nuisible est l'utilisation combinée de ces macros avec un opérateur de cast de style C. Si quelqu'un décide de convertir ces expressions boolen C ++ par exemple:

#include <iostream>

#define TRUE  '/'/'/'
#define FALSE '-'-'-'

int main() {
    std::cout << "True: " << (bool) TRUE << std::endl;
    std::cout << "False: " << (bool) FALSE << std::endl;
    return 0;
}

Voici ce que nous obtenons:

True: 0
False: -44

Donc (bool) TRUE, évaluerait réellement falseet (bool) FALSEévaluerait true.

BlackDwarf
la source
4
L'exemple est sympa :)
Kit Fisto
44

Cela équivaut à écrire

#define TRUE 1
#define FALSE 0

Ce que '/'/'/'fait réellement l'expression, c'est diviser le caractère /(quelle que soit sa valeur numérique) par lui-même, il devient donc 1.

De même, l'expression '-'-'-'soustrait le caractère -de lui-même et évalue à 0.

Il vaudrait mieux écrire

#define TRUE ('/'/'/')
#define FALSE ('-'-'-')

pour éviter un changement accidentel des valeurs lorsqu'il est utilisé avec d'autres opérateurs de priorité supérieure.

0605002
la source
5
LOL! C'est pourquoi les macros doivent être correctement entre parenthèses.
0605002 du
3 droits sur une sphère et vous vous retrouvez au même endroit
Derek 朕 會 功夫
@KerrekSB Non, mais trois gauches le font :)
Tim Long
33

Jay a déjà répondu pourquoi les valeurs de ces expressions sont 0et 1.

Par souci d'histoire, ces expressions '/'/'/'et '-'-'-'proviennent d'une des entrées du 1er Concours International de Code C Obfuscated en 1984 :

int i;main(){for(;i["]<i;++i){--i;}"];read('-'-'-',i+++"hell\
o, world!\n",'/'/'/'));}read(j,i,p){write(j/p+p,i---j,i/i);}

(Lien vers le programme ici , il y a un indice de ce que fait ce programme dans la page IOCCC ci-dessus.)

Aussi, si je me souviens bien, ces expressions étaient des macros obscurcies TRUEet FALSEétaient également couvertes dans le livre "Obfuscated C and Other Mysteries" de Don Libes (1993).

ouah
la source
7

C'est une façon hilarante d'écrire des macros pour Trueet False.

Comme de nombreuses explications ont été fournies, cela /signifie qu'un nombre de 1 octet (selon ASCII) lorsqu'il est divisé par lui-même, il vous donne 1qui sera traité comme Trueet -est également à nouveau un nombre d'octets lorsqu'il est soustrait de la même valeur qu'il vous donne 0qui sera interprétée commefalse

#define TRUE  '/'/'/'
#define FALSE '-'-'-'

par conséquent, nous pouvons remplacer /ou -avec n'importe quel caractère que nous aimons, par exemple:

#define TRUE  '!'/'!'
#define FALSE 'o'-'o'

Gardera la même signification que l'expression originale.

anand
la source
6

Commençons par vrai. Vous pouvez le lire comme '/' / '/', ce qui signifie "caractère '/' divisé par caractère '/'". Étant donné que chaque caractère, en C, est une valeur numérique (sur un octet), il peut être lu comme "la valeur ASCII du caractère '/' divisé par la valeur ASCII de ce même caractère", ce qui signifie 1 (car, évidemment, x / x est 1). Par conséquent, TRUEest 1.

Pour FALSE, c'est le même raisonnement: '-'-'-'lit '-' - '-', c'est-à-dire "la valeur ASCII de '-' moins la valeur ASCII de '-'", qui est 0. Par conséquent, FALSEest 0.

C'est une mauvaise façon de dire l'évidence.

Fabien
la source
7
Cela n'a rien à voir avec ASCII.
Kerrek SB
6
@Fabien: Cela ne dépend pas de l' ASCII. '/'/'/'est 1 pour tout jeu de caractères valide, que ce soit '/' == 47(comme c'est en ASCII), ou '/' == 97(comme c'est en EBCDIC), ou toute autre valeur.
Keith Thompson
4
@Pawel: Une mise en œuvre conforme C ne peut pas la carte '/'à 0. Cette valeur est réservée au caractère nul.
Keith Thompson
2
Vous êtes pédant.
Matheus208
3
@ Matheus208 Si Pawel était pédant, alors c'est ('-'-'-') puisque son argument était basé sur une condition non déclarée; décrire les remarques de Keith comme pédant pourrait être plus ('/' / '/') mais je les appellerais "clarifiant" (et avec les smileys ajoutés "pédant" me semble définitivement '/' - '/'). Il se peut ('-' / '-') que les commentaires pris ensemble puissent être appelés pédants mais, 1) n'est-ce pas quelque peu obligatoire dans ce domaine? 2) ils m'ont fait réfléchir; et 3) je suis un peu plus clair sur certaines choses que je ne l'étais. Et oui, je suppose que je suis moi-même pédant! (Mais je suis plus clair sur ce que signifie "pédant" que moi! ;-)
Zhora