Widziałem definicje w C
#define TRUE (1==1)
#define FALSE (!TRUE)
Czy to konieczne? Jaka jest korzyść z definiowania wartości TRUE jako 1 i FALSE jako 0?
c
precompiler
Robert Harvey
źródło
źródło
#define TRUE (’/’/’/’)
:;#define FALSE (’-’-’-’)
(zaczerpnięte z coding-guidelines.com/cbook/cbook1_1.pdf strona 871)Odpowiedzi:
To podejście użyje rzeczywistego
boolean
typu (i rozwiąże totrue
ifalse
), jeśli kompilator go obsługuje. (w szczególności C ++)Jednak lepiej byłoby sprawdzić, czy C ++ jest w użyciu (za pomocą
__cplusplus
makra) i czy faktycznie używatrue
ifalse
.W kompilatorze C jest to równoważne z
0
i1
.(pamiętaj, że usunięcie nawiasów przerwie to ze względu na kolejność operacji)
źródło
1==1
jestint
. (patrz stackoverflow.com/questions/7687403/… .)boolean
typem?true
lubfalse
.#define TRUE true
i#define FALSE false
kiedykolwiek byłby__cplusplus
zdefiniowany.Odpowiedzią jest przenośność. Wartości liczbowe
TRUE
iFALSE
nie są ważne. Co jest ważne to, że oświadczenie jakif (1 < 2)
ma wartośćif (TRUE)
oraz oświadczeniem jakif (1 > 2)
ma wartośćif (FALSE)
.To prawda, w C
(1 < 2)
oblicza1
i(1 > 2)
oblicza do0
, więc jak powiedzieli inni, nie ma praktycznej różnicy, jeśli chodzi o kompilator. Ale pozwalając kompilatorowi definiowaćTRUE
iFALSE
zgodnie z jego własnymi regułami, ujawniasz ich znaczenie programistom i gwarantujesz spójność w swoim programie i każdej innej bibliotece (zakładając, że inna biblioteka jest zgodna ze standardami C ... być zdumionym).Trochę historii
Niektóre BASIC-y zdefiniowane
FALSE
jako0
iTRUE
jako-1
. Podobnie jak wiele współczesnych języków, interpretowali każdą wartość niezerową jakoTRUE
, ale oceniali wyrażenia boolowskie, które były prawdziwe jako-1
. IchNOT
działanie zostało zrealizowane poprzez dodanie 1 i odwrócenie znaku, ponieważ było to wydajne. Stało się więc „NIE x”-(x+1)
. Efektem ubocznym jest to, że wartość taka jak5
szacuje się doTRUE
, aleNOT 5
szacuje się do-6
, co też jestTRUE
! Znalezienie tego rodzaju błędu nie jest zabawne.Najlepsze praktyki
Biorąc pod uwagę faktyczne zasady, że zero jest interpretowane jako,
FALSE
a każda wartość niezerowa jest interpretowana jakoTRUE
, nigdy nieTRUE
FALSE
należy porównywać wyrażeń wyglądających logicznie z lub . Przykłady:Czemu? Ponieważ wielu programistów używa skrótu do traktowania
int
s jakobool
s. Nie są takie same, ale kompilatory generalnie na to pozwalają. Na przykład pisanie jest całkowicie legalneŻe wygląda uzasadnione, a kompilator będzie szczęśliwie zaakceptować, ale to chyba nie robi tego, co tylko chcesz. Dzieje się tak, ponieważ zwracana wartość
strcmp()
to0 jeśli
yourString == myString
<0 jeśli
yourString < myString
> 0 jeśli
yourString > myString
Zatem powyższa linia zwraca
TRUE
tylko wtedy, gdyyourString > myString
.Właściwy sposób to zrobić
lub
Podobnie:
Często znajdziesz niektóre z tych „złych przykładów” w kodzie produkcyjnym i wielu doświadczonych programistów przysięga na nie: działają, niektórzy są krótsi niż ich (pedantycznie?) Poprawne alternatywy, a idiomy są prawie powszechnie rozpoznawane. Ale zastanów się: „właściwe” wersje nie są mniej wydajne, są przenośne, przejdą nawet najbardziej rygorystyczne lintery i nawet nowi programiści je zrozumieją.
Czy to nie jest tego warte?
źródło
(1==1)
nie jest bardziej przenośny niż1
. Własne reguły kompilatora są regułami języka C, który jest jasny i jednoznaczny w zakresie semantyki operatorów równości i relacyjnych. Nigdy nie widziałem, żeby kompilator źle to robił.strcmp
wiadomo, że wartość zwracana przez jest mniejsza niż, równa lub większa niż 0. Nie ma gwarancji, że będzie to -1, 0 lub 1, a istnieją platformy, które nie zwracają tych wartości, aby przyspieszyć implementację. Więc jeślistrcmp(a, b) == TRUE
wtedy,a > b
ale odwrotna implikacja może nie mieć miejsca.(1==1)
i1
oba są stałymi wyrażeniami typuint
o wartości 1. Są semantycznie identyczne. Przypuszczam, że możesz napisać kod przeznaczony dla czytelników, którzy tego nie wiedzą, ale gdzie to się kończy?Ta
(1 == 1)
sztuczka jest przydatna do definiowaniaTRUE
w sposób przezroczysty dla C, ale zapewnia lepsze pisanie w C ++. Ten sam kod może być zinterpretowany jako C lub C ++, jeśli piszesz w dialekcie zwanym „Clean C” (który kompiluje się jako C lub C ++) lub jeśli piszesz pliki nagłówkowe API, które mogą być używane przez programistów C lub C ++.W jednostkach tłumaczeniowych C
1 == 1
ma dokładnie to samo znaczenie, co1
; i1 == 0
ma takie samo znaczenie jak0
. Jednak w jednostkach tłumaczeniowych C ++1 == 1
ma typbool
. ZatemTRUE
zdefiniowane w ten sposób makro lepiej integruje się z C ++.Przykładem tego, jak lepiej integruje się, jest to, że na przykład jeśli funkcja
foo
ma przeciążenia dlaint
i dlabool
, tofoo(TRUE)
wybierzebool
przeciążenie. JeśliTRUE
jest zdefiniowane jako1
, to nie będzie dobrze działać w C ++.foo(TRUE)
będzie chciałint
przeciążenia.Oczywiście C99 wprowadził
bool
,true
ifalse
a te mogą być używane w plikach nagłówkowych, że praca z C99 i C.Jednak:
TRUE
iFALSE
jak(0==0)
i(1==0)
wyprzedza C99.Jeśli pracujesz w mieszanym projekcie C i C ++ i nie chcesz C99, zdefiniuj małe litery
true
,false
abool
zamiast tego.To powiedziawszy,
0==0
sztuczka była (jest?) Używana przez niektórych programistów nawet w kodzie, który nigdy nie był przeznaczony do współpracy z C ++ w jakikolwiek sposób. To nic nie kupuje i sugeruje, że programista ma niezrozumienie, jak wartości logiczne działają w C.Na wypadek, gdyby wyjaśnienie C ++ nie było jasne, oto program testowy:
Wyjście:
Co do pytania z komentarzy, w jaki sposób przeciążone są funkcje C ++ związane z programowaniem mieszanym C i C ++. To tylko ilustruje różnicę typów. Ważnym powodem, dla którego chcemy,
true
aby stała jestbool
kompilowana jako C ++, jest czysta diagnostyka. Przy najwyższych poziomach ostrzeżenia kompilator C ++ może ostrzec nas o konwersji, jeśli jakobool
parametr przekażemy liczbę całkowitą . Jednym z powodów pisania w Clean C jest nie tylko to, że nasz kod jest bardziej przenośny (ponieważ jest to zrozumiałe dla kompilatorów C ++, nie tylko C), ale możemy skorzystać z opinii diagnostycznych kompilatorów C ++.źródło
TRUE
będą się różnić w C ++.#ifdef __cplusplus
do wyraźniejszego wyrażenia swoich zamiarów.bool
iint
nie mają większego znaczenia w praktyce, ponieważ są one niejawnie konwertowane na siebie nawzajem (aw C faktycznie „to samo” , zwróć uwagę na cudzysłowy , choć) i nie ma wielu sytuacji, w których naprawdę trzeba by rozprawić się między nimi. „Niewiele” było prawdopodobnie zbyt ciężkie, „znacznie mniej w porównaniu z kodem używającym szablonów i przeciążeniem” mogłoby być lepsze.jest równa
w C.
Wynikiem operatorów relacyjnych jest
0
lub1
.1==1
ma gwarancję oceny1
i!(1==1)
ma gwarancję oceny0
.Nie ma absolutnie żadnego powodu, aby używać pierwszego formularza. Należy zauważyć, że pierwsza postać nie jest jednak mniej wydajna, ponieważ w prawie wszystkich kompilatorach wyrażenie stałe jest obliczane w czasie kompilacji, a nie w czasie wykonywania. Jest to dozwolone zgodnie z tą zasadą:
PC-Lint wyda nawet komunikat (506, stała wartość boolean), jeśli nie użyjesz literału for
TRUE
iFALSE
makr:Również w C99
stdbool.h
definicje makr boolowskichtrue
ifalse
bezpośrednio używają literałów:źródło
1==1
jest gwarantowany do oceny1
if(foo == true)
, który zmieni się od zwykłej złej praktyki do pełnego błędu.(x == TRUE)
może mieć inną wartość niżx
.Oprócz C ++ (już wspomnianego), kolejną korzyścią są narzędzia do analizy statycznej. Kompilator usunie wszelkie nieefektywności, ale analizator statyczny może używać własnych typów abstrakcyjnych do rozróżniania wyników porównania od innych typów całkowitych, dzięki czemu wie niejawnie, że PRAWDA musi być wynikiem porównania i nie należy zakładać, że jest zgodny z liczbą całkowitą.
Oczywiście C mówi, że są kompatybilne, ale możesz zabronić celowego korzystania z tej funkcji, aby pomóc w wyróżnieniu błędów - na przykład, gdy ktoś mógł pomylić
&
i&&
lub spartaczył pierwszeństwo swojego operatora.źródło
if (boolean_var == TRUE)
poprzez rozwinięcie, doif (boolean_var == (1 == 1))
którego dzięki rozszerzonej informacji o typie(1 == 1)
węzła wpada do wzorcaif (<*> == <boolean_expr>)
.Praktyczna różnica to żadna.
0
jest ocenianyfalse
i1
jest oceniany dotrue
. Fakt, że używasz wyrażenia logicznego (1 == 1
) lub1
, aby zdefiniowaćtrue
, nie robi żadnej różnicy. Obaj są ocenianiint
.Zauważ, że biblioteka standardowa C zapewnia nagłówek specyficzny dla zdefiniowania wartości logicznych:
stdbool.h
.źródło
true
jest oceniany1
ifalse
jest oceniany do0
. C nie wie o rodzimych typach boolowskich, są po prostu intami.int
, z wartością0
lub1
. C ma rzeczywisty typ boolowski (_Bool
z makrembool
zdefiniowanym w<stdbool.h>
, ale zostało dodane tylko w C99, co nie zmieniło semantyki operatorów, aby używały nowego typu._Bool
i<stdbool.h>
ma#define bool _Bool
.1 == 1
oceny jakoint
. Edytowano.Nie znamy dokładnej wartości równej TRUE, a kompilatory mogą mieć własne definicje. Więc to, co zamierzasz, to użyć wewnętrznego kompilatora do definicji. Nie zawsze jest to konieczne, jeśli masz dobre nawyki programistyczne, ale możesz uniknąć problemów związanych z pewnym złym stylem kodowania, na przykład:
if ((a> b) == TRUE)
Może to być katastrofą, jeśli ręcznie zdefiniujesz PRAWDA jako 1, podczas gdy wewnętrzna wartość PRAWDA będzie inna.
źródło
>
operator zawsze daje 1 dla prawdy, 0 dla fałszu. Nie ma możliwości, aby jakikolwiek kompilator C pomylił się. Porównania równości doTRUE
iFALSE
są w złym stylu; powyższe jest wyraźniej zapisane jakoif (a > b)
. Ale pomysł, że różne kompilatory C mogą inaczej traktować prawdę i fałsz, jest po prostu błędny.Zwykle w języku programowania C 1 jest definiowane jako prawda, a 0 jako fałsz. Dlatego dość często widzisz:
Jednak każda liczba różna od 0 zostanie uznana za prawdziwą również w instrukcji warunkowej. Dlatego korzystając z poniższych:
Możesz po prostu wyraźnie pokazać, że starasz się grać bezpiecznie, czyniąc fałsz równym temu, co nie jest prawdą.
źródło