Definições estranhas de macros TRUE e FALSE

300

Eu vi as seguintes definições de macro em um livro de codificação.

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

Não havia explicação lá.

Por favor, explique-me como isso funcionará como TRUEe FALSE.

Keshava GN
fonte
63
Eu acho que é apenas uma maneira engraçada para definir VERDADEIRO como 1 e FALSO como 0
BlackDwarf
160
Observe que essa é uma péssima idéia sem colchetes em torno dessas expressões. Quero dizer, é uma péssima ideia para eles, mas sem você está apenas pedindo uma longa noite de depuração.
TartanLlama
70
Posso saber o livro de codificação que você está referenciando?
Art
47
Espero que o livro inclua isso como um exemplo de código ruim ou deliberadamente obscuro.
Jon Hanna
31
@ Daniel: Outra idéia seria rand ()% 2 definir TALVEZ como rand ()% 2, de modo que às vezes é == VERDADEIRO e às vezes == FALSO.
Kaiserludi

Respostas:

380

Vamos ver: '/' / '/'significa o charliteral /, dividido pelo próprio charliteral '/'. O resultado é um, que parece razoável TRUE.

E '-' - '-'significa o charliteral '-', subtraído de si mesmo. Isso é zero ( FALSE).

Existem dois problemas com isso: primeiro, não é legível. Usando 1e 0é absolutamente melhor. Além disso, como o TartanLlama e o KerrekSB apontaram, se você alguma vez usar essa definição, adicione parênteses em torno deles para não ter surpresas:

#include <stdio.h>

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

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

Isso imprimirá o valor do charliteral '-'(45 no meu sistema).

Entre parênteses:

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

o programa imprime corretamente zero, mesmo que não faça muito sentido multiplicar um valor de verdade por um número inteiro, mas é apenas um exemplo do tipo de bugs inesperados que poderiam mordê-lo se você não colocar parênteses suas macros.

Jay
fonte
6
Droga Eu tomei um monte de compreendê-lo: é ainda pensei que era uma coisa estranha como glifos ... não sei xD
Luis Masuelli
8
Na verdade, faz sentido multiplicar com o valor da verdade. Para o recuo do exame * should_indent resultará em 0 ou recuo, com base em se deveria_indent sem ramificação. (Acho que esse é um mau exemplo, ao trabalhar com ramificação única de texto não importa, (vi essa técnica em shaders e no XPATH (ambos muito diferentes e não me lembro da forma exata))
Alpedar
2
Alpedar - mas não conceitualmente e matematicamente faz sentido fazer isso - nesse caso, seria mais claro (e conceitualmente faz sentido) usar um em ifvez de multiplicar TRUEpor um número inteiro.
Jay
4
Ótima explicação. Tenha um crachá de ouro!
Michael Hampton
2
A negação lógica pode ser implementada notx = TRUE- x;e funciona bem. Excepto que TRUE-FALSEé -44 (ASCII assumindo)
Hagen von Eitzen
89

É apenas outra maneira de escrever

#define TRUE 1
#define FALSE 0

A expressão '/'/'/'dividirá o valor do caractere '/'por si só, o que fornecerá 1 como resultado.

A expressão '-'-'-'subtrairá o valor de char de '-'si mesmo, o que fornecerá 0 como resultado.

No defineentanto, faltam colchetes em torno das expressões inteiras , o que pode levar a erros no código usando essas macros. A resposta de Jay aborda isso muito bem.

Um exemplo de cenário "real" em que esquecer os colchetes pode ser prejudicial é o uso combinado dessas macros com um operador de conversão no estilo C. Se alguém decidir converter essas expressões boolno C ++, por exemplo:

#include <iostream>

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

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

Aqui está o que temos:

True: 0
False: -44

Então, (bool) TRUEseria realmente avaliar falsee (bool) FALSEavaliar true.

BlackDwarf
fonte
4
O exemplo é bom :)
Kit Fisto 6/16
44

É equivalente a escrever

#define TRUE 1
#define FALSE 0

O que a expressão '/'/'/'realmente faz é dividir o caractere /(qualquer que seja seu valor numérico) por si mesmo, para que ele se torne 1.

Da mesma forma, a expressão '-'-'-'subtrai o caractere -de si e avalia como 0.

Seria melhor escrever

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

para evitar alterações acidentais de valores quando usadas com outros operadores de alta precedência.

0605002
fonte
5
RI MUITO! É por isso que as macros devem estar entre parênteses.
0605002
3 direitos em uma esfera e você acaba no mesmo lugar
Derek #
@KerrekSB Não, mas três esquerdas fazem :) #
Tim Long
33

Jay já respondeu por que os valores dessas expressões são 0e 1.

Para a história bem, estas expressões '/'/'/'e '-'-'-'vêm de uma das entradas de Concurso código ofuscado C 1st International, em 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);}

(Link para o programa aqui , há uma dica do que esse programa faz na página da IOCCC acima.)

Além disso, se eu me lembro corretamente dessas expressões como macros ofuscadas TRUEe FALSEtambém foram abordadas no livro "Obfuscated C and Other Mysteries" de Don Libes (1993).

ouah
fonte
7

É uma maneira hilária de escrever macros para TrueeFalse .

Como muitas explicações foram fornecidas /, um número de 1 byte (conforme ASCII), quando dividido por si só, fornece a você o 1que será tratado Truee, da mesma forma, -é novamente um número de byte ao subtrair o mesmo valor 0que será interpretado comofalse

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

portanto, podemos substituir /ou -por qualquer caractere que desejarmos, por exemplo:

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

Manterá o mesmo significado que a expressão original.

anand
fonte
6

Vamos começar com true. Você pode lê-lo como '/' / '/', o que significa "caractere '/' dividido pelo caractere '/'". Como cada caractere, em C, é um valor numérico (em um byte), ele pode ser lido como "o valor ASCII do caractere '/' dividido pelo valor ASCII desse mesmo caractere", o que significa 1 (porque, obviamente, x / x é 1). Conseqüentemente,TRUE é 1.

Pois FALSE, é o mesmo raciocínio: '-'-'-''-' - '-', ie "o valor ASCII de '-' menos o valor ASCII de '-'", que é 0. Portanto, FALSEé 0.

Esta é uma maneira desagradável de afirmar o óbvio.

Fabien
fonte
7
Isso não tem nada a ver com ASCII.
Kerrek SB
6
@Fabien: Não depende de ASCII. '/'/'/'é 1 para qualquer conjunto de caracteres válido, seja '/' == 47(como em ASCII) ou '/' == 97(como em EBCDIC) ou qualquer outro valor.
Keith Thompson
4
@Pawel: Uma implementação conforme C não pode mapear '/'a 0. Esse valor é reservado para o caractere nulo.
Keith Thompson
2
Você está sendo pedante.
precisa saber é o seguinte
3
@ Matheus208 Se Pawel estava sendo pedante, então isso é ('-'-'-'), já que seu argumento foi baseado em uma condição não declarada; descrever os comentários de Keith como pedantes pode ser mais ('/' / '/'), mas eu os chamaria de "esclarecedor" (e com os smileys adicionados "pedante" definitivamente me parece '/' - '/'). Pode ser ('-' / '-') que os comentários juntos possam ser chamados de pedantes, mas, 1) isso não é algo obrigatório nesse campo? 2) eles me fizeram pensar; e 3) sou um pouco mais claro em algumas coisas do que era. E sim, acho que estou sendo pedante! (Mas eu sou mais claro sobre o que "pedante" significa do que eu era! ;-) #
244 Zhora Zhora