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?
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?
Odpowiedzi:
To podejście użyje rzeczywistego boolean
typu (i rozwiąże to true
i false
), 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żywa true
i false
.
W kompilatorze C jest to równoważne z 0
i 1
.
(pamiętaj, że usunięcie nawiasów przerwie to ze względu na kolejność operacji)
1==1
jest int
. (patrz stackoverflow.com/questions/7687403/… .)
boolean
typem?
true
lub false
.
#define TRUE true
i #define FALSE false
kiedykolwiek byłby __cplusplus
zdefiniowany.
Odpowiedzią jest przenośność. Wartości liczbowe TRUE
i FALSE
nie są ważne. Co jest ważne to, że oświadczenie jak if (1 < 2)
ma wartość if (TRUE)
oraz oświadczeniem jak if (1 > 2)
ma wartość if (FALSE)
.
To prawda, w C (1 < 2)
oblicza 1
i (1 > 2)
oblicza do 0
, więc jak powiedzieli inni, nie ma praktycznej różnicy, jeśli chodzi o kompilator. Ale pozwalając kompilatorowi definiować TRUE
i FALSE
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
jako 0
i TRUE
jako -1
. Podobnie jak wiele współczesnych języków, interpretowali każdą wartość niezerową jako TRUE
, ale oceniali wyrażenia boolowskie, które były prawdziwe jako -1
. Ich NOT
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 jak 5
szacuje się do TRUE
, ale NOT 5
szacuje się do -6
, co też jest TRUE
! 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 jako TRUE
, nigdy nieTRUE
FALSE
należy porównywać wyrażeń wyglądających logicznie z lub . Przykłady:
if (thisValue == FALSE) // Don't do this!
if (thatValue == TRUE) // Or this!
if (otherValue != TRUE) // Whatever you do, don't do this!
Czemu? Ponieważ wielu programistów używa skrótu do traktowania int
s jako bool
s. Nie są takie same, ale kompilatory generalnie na to pozwalają. Na przykład pisanie jest całkowicie legalne
if (strcmp(yourString, myString) == TRUE) // Wrong!!!
Ż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()
to
0 jeśli yourString == myString
<0 jeśli yourString < myString
> 0 jeśliyourString > myString
Zatem powyższa linia zwraca TRUE
tylko wtedy, gdy yourString > myString
.
Właściwy sposób to zrobić
// Valid, but still treats int as bool.
if (strcmp(yourString, myString))
lub
// Better: lingustically clear, compiler will optimize.
if (strcmp(yourString, myString) != 0)
Podobnie:
if (someBoolValue == FALSE) // Redundant.
if (!someBoolValue) // Better.
return (x > 0) ? TRUE : FALSE; // You're fired.
return (x > 0); // Simpler, clearer, correct.
if (ptr == NULL) // Perfect: compares pointers.
if (!ptr) // Sleazy, but short and valid.
if (ptr == FALSE) // Whatisthisidonteven.
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?
(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śli strcmp(a, b) == TRUE
wtedy, a > b
ale odwrotna implikacja może nie mieć miejsca.
(1==1)
i 1
oba są stałymi wyrażeniami typu int
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 definiowania TRUE
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, co 1
; i 1 == 0
ma takie samo znaczenie jak 0
. Jednak w jednostkach tłumaczeniowych C ++ 1 == 1
ma typ bool
. Zatem TRUE
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 dla int
i dla bool
, to foo(TRUE)
wybierze bool
przeciążenie. Jeśli TRUE
jest zdefiniowane jako 1
, to nie będzie dobrze działać w C ++. foo(TRUE)
będzie chciał int
przeciążenia.
Oczywiście C99 wprowadził bool
, true
i false
a te mogą być używane w plikach nagłówkowych, że praca z C99 i C.
Jednak:
TRUE
i FALSE
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
a bool
zamiast tego.
#ifndef __cplusplus
typedef int bool;
#define true (0==0)
#define false (!true)
#endif
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:
#include <cstdio>
void foo(bool x)
{
std::puts("bool");
}
void foo(int x)
{
std::puts("int");
}
int main()
{
foo(1 == 1);
foo(1);
return 0;
}
Wyjście:
bool
int
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 jest bool
kompilowana jako C ++, jest czysta diagnostyka. Przy najwyższych poziomach ostrzeżenia kompilator C ++ może ostrzec nas o konwersji, jeśli jako bool
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 ++.
TRUE
będą się różnić w C ++.
#ifdef __cplusplus
do wyraźniejszego wyrażenia swoich zamiarów.
bool
i int
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.
#define TRUE (1==1)
#define FALSE (!TRUE)
jest równa
#define TRUE 1
#define FALSE 0
w C.
Wynikiem operatorów relacyjnych jest 0
lub 1
. 1==1
ma gwarancję oceny 1
i !(1==1)
ma gwarancję oceny 0
.
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ą:
(C99, 6.6p2) „Wyrażenie stałe może być oceniane podczas tłumaczenia, a nie w czasie wykonywania, i odpowiednio może być używane w dowolnym miejscu, w którym może być stała”.
PC-Lint wyda nawet komunikat (506, stała wartość boolean), jeśli nie użyjesz literału for TRUE
i FALSE
makr:
Dla C
TRUE
należy zdefiniować jako1
. Jednak inne języki używają wartości innych niż 1, więc niektórzy programiści uważają, że!0
jest to bezpieczne.
Również w C99 stdbool.h
definicje makr boolowskich true
i false
bezpośrednio używają literałów:
#define true 1
#define false 0
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.
if (boolean_var == TRUE)
poprzez rozwinięcie, do if (boolean_var == (1 == 1))
którego dzięki rozszerzonej informacji o typie (1 == 1)
węzła wpada do wzorca if (<*> == <boolean_expr>)
.
Praktyczna różnica to żadna. 0
jest oceniany false
i 1
jest oceniany do true
. Fakt, że używasz wyrażenia logicznego ( 1 == 1
) lub 1
, aby zdefiniować true
, nie robi żadnej różnicy. Obaj są oceniani int
.
Zauważ, że biblioteka standardowa C zapewnia nagłówek specyficzny dla zdefiniowania wartości logicznych: stdbool.h
.
true
jest oceniany 1
i false
jest oceniany do 0
. C nie wie o rodzimych typach boolowskich, są po prostu intami.
int
, z wartością 0
lub 1
. C ma rzeczywisty typ boolowski ( _Bool
z makrem bool
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 jako int
. 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.
>
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 do TRUE
i FALSE
są w złym stylu; powyższe jest wyraźniej zapisane jako if (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:
#define TRUE 1
#define FALSE 0
Jednak każda liczba różna od 0 zostanie uznana za prawdziwą również w instrukcji warunkowej. Dlatego korzystając z poniższych:
#define TRUE (1==1)
#define FALSE (!TRUE)
Możesz po prostu wyraźnie pokazać, że starasz się grać bezpiecznie, czyniąc fałsz równym temu, co nie jest prawdą.
#define TRUE (’/’/’/’)
:;#define FALSE (’-’-’-’)
(zaczerpnięte z coding-guidelines.com/cbook/cbook1_1.pdf strona 871)