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 booleantypu (i rozwiąże to truei 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ą __cplusplusmakra) i czy faktycznie używa truei false.
W kompilatorze C jest to równoważne z 0i 1.
(pamiętaj, że usunięcie nawiasów przerwie to ze względu na kolejność operacji)
1==1jest int. (patrz stackoverflow.com/questions/7687403/… .)
booleantypem?
truelub false.
#define TRUE truei #define FALSE falsekiedykolwiek byłby __cpluspluszdefiniowany.
Odpowiedzią jest przenośność. Wartości liczbowe TRUEi FALSEnie 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 1i (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ć TRUEi FALSEzgodnie 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 FALSEjako 0i TRUEjako -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 NOTdział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 5szacuje się do TRUE, ale NOT 5szacuje 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, FALSEa każda wartość niezerowa jest interpretowana jako TRUE, nigdy nieTRUEFALSE 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 ints jako bools. 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 TRUEtylko 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ł.
strcmpwiadomo, ż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) == TRUEwtedy, a > bale odwrotna implikacja może nie mieć miejsca.
(1==1)i 1oba są stałymi wyrażeniami typu into 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 TRUEw 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 == 1ma dokładnie to samo znaczenie, co 1; i 1 == 0ma takie samo znaczenie jak 0. Jednak w jednostkach tłumaczeniowych C ++ 1 == 1ma typ bool. Zatem TRUEzdefiniowane 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 fooma przeciążenia dla inti dla bool, to foo(TRUE)wybierze boolprzeciążenie. Jeśli TRUEjest zdefiniowane jako 1, to nie będzie dobrze działać w C ++. foo(TRUE)będzie chciał intprzeciążenia.
Oczywiście C99 wprowadził bool, truei falsea te mogą być używane w plikach nagłówkowych, że praca z C99 i C.
Jednak:
TRUEi FALSEjak (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, falsea boolzamiast tego.
#ifndef __cplusplus
typedef int bool;
#define true (0==0)
#define false (!true)
#endif
To powiedziawszy, 0==0sztuczka 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, trueaby stała jest boolkompilowana jako C ++, jest czysta diagnostyka. Przy najwyższych poziomach ostrzeżenia kompilator C ++ może ostrzec nas o konwersji, jeśli jako boolparametr 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 ++.
TRUEbędą się różnić w C ++.
#ifdef __cplusplusdo wyraźniejszego wyrażenia swoich zamiarów.
booli intnie 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 0lub 1. 1==1ma gwarancję oceny 1i !(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 TRUEi FALSEmakr:
Dla C
TRUEnależy zdefiniować jako1. Jednak inne języki używają wartości innych niż 1, więc niektórzy programiści uważają, że!0jest to bezpieczne.
Również w C99 stdbool.hdefinicje makr boolowskich truei false bezpośrednio używają literałów:
#define true 1
#define false 0
1==1jest 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. 0jest oceniany falsei 1jest 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.
truejest oceniany 1i falsejest oceniany do 0. C nie wie o rodzimych typach boolowskich, są po prostu intami.
int, z wartością 0lub 1. C ma rzeczywisty typ boolowski ( _Boolz makrem boolzdefiniowanym w <stdbool.h>, ale zostało dodane tylko w C99, co nie zmieniło semantyki operatorów, aby używały nowego typu.
_Booli <stdbool.h>ma #define bool _Bool.
1 == 1oceny 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 TRUEi FALSEsą 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)