Dziwne definicje makr PRAWDA i FAŁSZ

300

W książce kodowej widziałem następujące definicje makr.

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

Nie było tam żadnego wyjaśnienia.

Proszę wyjaśnić mi, jak będą one działać jako TRUEi FALSE.

Keshava GN
źródło
63
Myślę, że jest to po prostu zabawny sposób zdefiniowania PRAWDA jako 1, a FAŁSZ jako 0
BlackDwarf
160
Zauważ, że to okropny pomysł bez nawiasów wokół tych wyrażeń. Mam na myśli to okropny pomysł z nimi, ale bez pytania o długą noc debugowania.
TartanLlama,
70
Czy mogę poznać książkę, do której się odwołujesz?
artm
47
Mam nadzieję, że książka zawiera to jako przykład złego lub celowo niejasnego kodu.
Jon Hanna,
31
@Daniel: Innym pomysłem byłoby rand ()% 2 zdefiniowanie MAYBE jako rand ()% 2, więc czasami jest to == PRAWDA, a czasami == FAŁSZ.
Kaiserludi,

Odpowiedzi:

380

Zobaczmy: '/' / '/'oznacza charliterał /podzielony przez sam charliterał '/'. Wynik jest taki, co wydaje się rozsądne TRUE.

I '-' - '-'oznacza chardosłowne '-', odjęte od siebie. To zero ( FALSE).

Są z tym dwa problemy: po pierwsze, nie można go odczytać. Korzystanie 1i 0jest absolutnie lepsze. Ponadto, jak zauważyli TartanLlama i KerrekSB, jeśli kiedykolwiek będziesz używać tej definicji, dodaj nawiasy wokół nich, abyś nie miał żadnych niespodzianek:

#include <stdio.h>

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

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

Spowoduje to wydrukowanie wartości charliterału '-'(45 w moim systemie).

Z nawiasami:

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

program poprawnie drukuje zero, mimo że nie ma większego sensu mnożenie wartości prawdy przez liczbę całkowitą, ale jest to tylko przykład nieoczekiwanych błędów, które mogą cię ugryźć, jeśli nie nawiasujesz makr w nawiasie.

Sójka
źródło
6
Cholera, dużo czasu zajęło mi zrozumienie: nawet myślałem, że to dziwna rzecz jako glify ... nie wiem xD
Luis Masuelli
8
Właściwie sensowne jest mnożenie z wartością prawdy. Na przykład wcięcie * should_indent spowoduje albo 0, albo wcięcie na podstawie tego, czy should_indent bez rozgałęzienia. (Wydaje mi się, że jest to zły przykład, gdy praca z tekstem rozgałęzianie pojedyncze nie ma znaczenia, (Widziałem tę technikę w shaderach i XPATH (zarówno zbyt różne, jak i nie pamiętam dokładnej formy))
Alpedar
2
Alpedar - ale nie ma sensu czynić tego pojęciowo i matematycznie - w tym przypadku bardziej zrozumiałe (i koncepcyjnie sensowne) jest użycie ifzamiast mnożenia TRUEprzez liczbę całkowitą.
Jay
4
Świetne wyjaśnienie. Zdobądź złotą odznakę!
Michael Hampton
2
Logiczna negacja może być zaimplementowana jako notx = TRUE- x;i działa dobrze. Tyle że TRUE-FALSE-44 (przy założeniu ASCII)
Hagen von Eitzen
89

To tylko inny sposób pisania

#define TRUE 1
#define FALSE 0

Wyrażenie '/'/'/'samo podzieli wartość char '/', co w rezultacie da 1.

Wyrażenie '-'-'-'odejmie '-'od siebie wartość char , co w rezultacie da 0.

defineBrakuje jednak nawiasów wokół całych wyrażeń, co może prowadzić do błędów w kodzie przy użyciu tych makr. Odpowiedź Jaya całkiem dobrze na to odpowiada.

Przykładem „rzeczywistego” scenariusza, w którym zapomnienie nawiasów klamrowych może być szkodliwe, jest łączenie użycia tych makr z operatorem rzutowania w stylu C. Jeśli na przykład ktoś zdecyduje się rzutować na te wyrażenia boolw C ++:

#include <iostream>

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

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

Oto, co otrzymujemy:

True: 0
False: -44

Więc (bool) TRUEfaktycznie oceniał falsei (bool) FALSEoceniał true.

BlackDwarf
źródło
4
Przykład jest fajny :)
Kit Fisto
44

Jest to równoważne z pisaniem

#define TRUE 1
#define FALSE 0

To, co '/'/'/'faktycznie robi wyrażenie , polega na podzieleniu znaku /(bez względu na jego wartość liczbową), więc staje się 1.

Podobnie, wyrażenie '-'-'-'odejmuje znak -od siebie i ocenia na 0.

Lepiej byłoby pisać

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

aby uniknąć przypadkowej zmiany wartości w przypadku użycia z innymi operatorami o wyższym priorytecie.

0605002
źródło
5
LOL! Dlatego makra powinny być odpowiednio nawiasowane.
0605002
3 prawa do kuli i
wylądujesz
@KrekrekSB Nie, ale trzy lewe tak :)
Tim Long
33

Jay już odpowiedział, dlaczego wartości tych wyrażeń to 0i 1.

Ze względów historycznych te wyrażenia '/'/'/' i '-'-'-'pochodzą z jednego z wpisów z 1st International Code zamaskowany C konkursu w 1984 roku :

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 do programu tutaj , podpowiedź tego, co ten program robi na stronie IOCCC powyżej).

Również jeśli dobrze pamiętam te wyrażenia jako zaciemnione makra dla TRUEi FALSEzostały one również omówione w książce Don Obcuscated C and Other Mysteries autorstwa Don Libesa (1993).

ouah
źródło
7

To zabawny sposób pisania makr dla Truei False.

Ponieważ podano wiele wyjaśnień, /oznacza to, że 1-bajtowa liczba (zgodnie z ASCII) po podzieleniu przez siebie daje ci to, 1co będzie traktowane, Truei podobnie -jest znowu liczbą bajtów po odjęciu tej samej wartości, 0którą ci da, która zostanie zinterpretowana jakofalse

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

dlatego możemy zastąpić /lub -dowolnym char, który nam się podoba, na przykład:

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

Zachowuje to samo znaczenie, co oryginalne wyrażenie.

anand
źródło
6

Zacznijmy od prawdy. Możesz go odczytać jako '/' / '/', co oznacza „znak” / „podzielony przez znak” / „”. Ponieważ każdy znak w C jest wartością liczbową (w jednym bajcie), można go odczytać jako „wartość ASCII znaku” / ”podzieloną przez wartość ASCII tego samego znaku”, co oznacza 1 (ponieważ, oczywiście, x / x to 1). Stąd TRUEjest 1.

Ponieważ FALSEma to samo rozumowanie: '-'-'-'czyta '-' - '-', tj. „Wartość ASCII z„ - ”minus wartość ASCII z„ - ”„, która wynosi 0. Stąd FALSEwynosi 0.

To paskudny sposób na stwierdzenie oczywistości.

Fabien
źródło
7
To nie ma nic wspólnego z ASCII.
Kerrek SB
6
@Fabien: To nie zależy od ASCII. '/'/'/'wynosi 1 dla dowolnego prawidłowego zestawu znaków, czy to '/' == 47(jak w ASCII), '/' == 97(jak w EBCDIC), czy jakakolwiek inna wartość.
Keith Thompson,
4
@Pawel: Implementacja zgodna C nie można mapować '/'do 0. Ta wartość jest zarezerwowana dla znaku zerowego.
Keith Thompson,
2
Jesteś pedantyczny.
Matheus208,
3
@ Matheus208 Jeśli Paweł był pedantyczny, to („-'-”-”), ponieważ jego punkt widzenia był oparty na nieokreślonym stanie; opisywanie uwag Keitha jako pedantycznych może być więcej („/” / „/”), ale nazwałbym je „wyjaśniającymi” (a wraz z dodanymi buźkami „pedantyczny” zdecydowanie wydaje mi się „/” - ”/”). Może być („-” / „-”), że zebrane komentarze można nazwać pedantycznymi, ale: 1) czy nie jest to nieco obowiązkowe w tej dziedzinie? 2) zmusili mnie do myślenia; i 3) Jestem trochę jaśniejszy w niektórych sprawach niż byłem. I tak, chyba sam jestem pedantyczny! (Ale jaśniej rozumiem, co znaczy „pedantyczny” niż byłem! ;-)
Zhora