Ho visto le definizioni in C
#define TRUE (1==1)
#define FALSE (!TRUE)
È necessario? Qual è il vantaggio rispetto alla semplice definizione di TRUE come 1 e FALSE come 0?
Ho visto le definizioni in C
#define TRUE (1==1)
#define FALSE (!TRUE)
È necessario? Qual è il vantaggio rispetto alla semplice definizione di TRUE come 1 e FALSE come 0?
Risposte:
Questo approccio utilizzerà il boolean
tipo effettivo (e risolverà in true
e false
) se il compilatore lo supporta. (in particolare, C ++)
Tuttavia, sarebbe meglio verificare se C ++ è in uso (tramite la __cplusplus
macro) e effettivamente utilizzare true
e false
.
In un compilatore C, questo equivale a 0
e 1
.
(notare che la rimozione delle parentesi interromperà ciò a causa dell'ordine delle operazioni)
1==1
è un int
. (vedi stackoverflow.com/questions/7687403/… .)
boolean
tipo?
true
o false
.
#define TRUE true
e #define FALSE false
ogni volta che __cplusplus
viene definito.
La risposta è portabilità. I valori numerici TRUE
e FALSE
non sono importanti. Ciò che è importante è che un'affermazione come if (1 < 2)
valuta if (TRUE)
e un'affermazione come if (1 > 2)
valuta if (FALSE)
.
Concesso, in C, (1 < 2)
valuta 1
e (1 > 2)
valuta 0
, così come altri hanno detto, non c'è differenza pratica per quanto riguarda il compilatore. Ma lasciando che il compilatore definisca TRUE
e FALSE
secondo le proprie regole, stai esplicitando i loro significati per i programmatori e stai garantendo coerenza all'interno del tuo programma e di qualsiasi altra libreria (supponendo che l'altra libreria segua gli standard C ... essere stupito).
Un po 'di storia
Alcuni BASIC definiti FALSE
come 0
e TRUE
come -1
. Come molti linguaggi moderni, interpretavano qualsiasi valore diverso da zero TRUE
, ma valutavano espressioni booleane come vere -1
. La loro NOT
operazione è stata implementata aggiungendo 1 e capovolgendo il segno, perché era efficiente farlo in quel modo. Così divenne 'NOT x' -(x+1)
. Un effetto collaterale di questo è che un valore come 5
valuta TRUE
, ma NOT 5
valuta -6
, e lo è anche TRUE
! Trovare questo tipo di bug non è divertente.
Best practice
Date le regole di fatto che lo zero viene interpretato come FALSE
e qualsiasi valore diverso da zero viene interpretato come TRUE
, non si dovrebbe mai confrontare espressioni dall'aspetto booleano con TRUE
oFALSE
. Esempi:
if (thisValue == FALSE) // Don't do this!
if (thatValue == TRUE) // Or this!
if (otherValue != TRUE) // Whatever you do, don't do this!
Perché? Perché molti programmatori usano la scorciatoia per trattare int
s come bool
s. Non sono gli stessi, ma i compilatori generalmente lo consentono. Quindi, per esempio, è perfettamente legale scrivere
if (strcmp(yourString, myString) == TRUE) // Wrong!!!
Che sguardi legittimi, e il compilatore sarà lieto di accettarlo, ma probabilmente non fa quello che ci si vuole. Questo perché il valore restituito di strcmp()
è
0 if yourString == myString
<0 if yourString < myString
> 0 ifyourString > myString
Quindi la riga sopra ritorna TRUE
solo quando yourString > myString
.
Il modo giusto per farlo è neanche
// Valid, but still treats int as bool.
if (strcmp(yourString, myString))
o
// Better: lingustically clear, compiler will optimize.
if (strcmp(yourString, myString) != 0)
Allo stesso modo:
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.
Troverai spesso alcuni di questi "cattivi esempi" nel codice di produzione, e molti programmatori esperti lo giurano: funzionano, alcuni sono più brevi delle loro alternative (pedanticamente?) Corrette e gli idiomi sono quasi universalmente riconosciuti. Ma considera: le versioni "giuste" non sono meno efficienti, sono garantite per essere portatili, passeranno anche le linters più rigorose e anche i nuovi programmatori le capiranno.
Non ne vale la pena?
(1==1)
non è più portatile di 1
. Le regole del compilatore sono quelle del linguaggio C, che è chiaro e inequivocabile sulla semantica dell'uguaglianza e degli operatori relazionali. Non ho mai visto un compilatore sbagliare questa roba.
strcmp
è noto essere inferiore, uguale o maggiore di 0. Non è garantito che sia -1, 0 o 1 e ci sono piattaforme in natura che non restituiscono quei valori per ottenere velocità di implementazione. Quindi, se strcmp(a, b) == TRUE
poi, a > b
ma l'implicazione inversa potrebbe non essere valida.
(1==1)
e 1
sono entrambe espressioni costanti di tipo int
con il valore 1. Sono semanticamente identiche. Suppongo che tu possa scrivere codice che si rivolge ai lettori che non lo sanno, ma dove finisce?
Il (1 == 1)
trucco è utile per definire TRUE
in modo trasparente a C, ma fornisce una migliore digitazione in C ++. Lo stesso codice può essere interpretato come C o C ++ se si scrive in un dialetto chiamato "Clean C" (che viene compilato come C o C ++) o se si stanno scrivendo file di intestazione API che possono essere utilizzati dai programmatori C o C ++.
In unità di traduzione C, 1 == 1
ha esattamente lo stesso significato di 1
; e 1 == 0
ha lo stesso significato di 0
. Tuttavia, nelle unità di traduzione C ++, 1 == 1
ha tipo bool
. Quindi la TRUE
macro definita in questo modo si integra meglio in C ++.
Un esempio di come si integra meglio è che, ad esempio, se la funzione foo
ha sovraccarichi per int
e per bool
, allora foo(TRUE)
sceglierà il bool
sovraccarico. Se TRUE
è appena definito come 1
, allora non funzionerà bene nel C ++. foo(TRUE)
vorrà il int
sovraccarico.
Naturalmente, C99 ha introdotto bool
, true
e false
e questi possono essere utilizzati nei file di intestazione che funzionano con C99 e con C.
Però:
TRUE
e FALSE
come (0==0)
e (1==0)
precede C99.Se si lavora in un C mista e di progetto C ++, e non volete C99, definire la minuscola true
, false
e bool
invece.
#ifndef __cplusplus
typedef int bool;
#define true (0==0)
#define false (!true)
#endif
Detto questo, il 0==0
trucco è stato (è?) Utilizzato da alcuni programmatori anche nel codice che non è mai stato progettato per interagire con C ++ in alcun modo. Ciò non compra nulla e suggerisce che il programmatore abbia un fraintendimento di come funzionano i booleani in C.
Nel caso in cui la spiegazione del C ++ non fosse chiara, ecco un programma di test:
#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;
}
Il risultato:
bool
int
Per quanto riguarda la domanda dai commenti su come sono sovraccaricate le funzioni C ++ rilevanti per la programmazione mista C e C ++. Questi illustrano solo una differenza di tipo. Un motivo valido per volere che una true
costante sia bool
compilata come C ++ è per una diagnostica pulita. Ai massimi livelli di avviso, un compilatore C ++ potrebbe avvisarci di una conversione se passiamo un numero intero come bool
parametro. Un motivo per scrivere in Clean C non è solo il fatto che il nostro codice è più portabile (poiché è compreso dai compilatori C ++, non solo dai compilatori C), ma possiamo trarre vantaggio dalle opinioni diagnostiche dei compilatori C ++.
TRUE
differiranno in C ++.
#ifdef __cplusplus
per esprimere le tue intenzioni in modo molto più chiaro.
bool
e int
non contano molto in pratica, dal momento che sono implicitamente convertibili tra loro (e in C in realtà "lo stesso" , nota le virgolette , però) e non ci sono molte situazioni in cui è davvero necessario disambivare tra i due. "non molto" era probabilmente troppo pesante, "molto meno rispetto al codice che utilizzava template e sovraccarico" forse sarebbe stato meglio.
#define TRUE (1==1)
#define FALSE (!TRUE)
è equivalente a
#define TRUE 1
#define FALSE 0
in C.
Il risultato degli operatori relazionali è 0
o 1
. 1==1
è garantito per essere valutato 1
ed !(1==1)
è garantito per essere valutato 0
.
Non c'è assolutamente alcun motivo per utilizzare il primo modulo. Si noti che il primo modulo non è tuttavia meno efficiente poiché in quasi tutti i compilatori viene valutata un'espressione costante in fase di compilazione anziché in fase di esecuzione. Questo è consentito secondo questa regola:
(C99, 6,6p2) "Un'espressione di costante può essere valutata durante la traduzione piuttosto che in fase di esecuzione, e di conseguenza può essere utilizzata in qualsiasi luogo in cui possa essere una costante."
PC-Lint emetterà anche un messaggio (506, valore booleano costante) se non si usa un valore letterale per TRUE
e FALSE
macro:
Per C,
TRUE
dovrebbe essere definito come1
. Tuttavia, altre lingue usano quantità diverse da 1, quindi alcuni programmatori ritengono che lo!0
stia giocando in modo sicuro.
Sempre in C99, le stdbool.h
definizioni per le macro booleane true
e false
usano direttamente i letterali:
#define true 1
#define false 0
1==1
è garantito per essere valutato a1
if(foo == true)
, che andrà dalla semplice cattiva pratica al buggy a tutto campo.
(x == TRUE)
può avere un valore di verità diverso da x
.
Oltre al C ++ (già menzionato), un altro vantaggio è rappresentato dagli strumenti di analisi statica. Il compilatore eliminerà qualsiasi inefficienza, ma un analizzatore statico può utilizzare i propri tipi astratti per distinguere tra risultati del confronto e altri tipi interi, quindi sa implicitamente che TRUE deve essere il risultato di un confronto e non deve essere considerato compatibile con un numero intero.
Ovviamente C afferma che sono compatibili, ma puoi scegliere di vietare l'uso deliberato di quella funzione per evidenziare i bug, ad esempio dove qualcuno potrebbe avere confuso &
e &&
, o hanno confuso la precedenza dell'operatore.
if (boolean_var == TRUE)
attraverso l'espansione a if (boolean_var == (1 == 1))
cui grazie alle informazioni di tipo avanzate del (1 == 1)
nodo rientrano nel modello if (<*> == <boolean_expr>)
.
La differenza pratica è nessuna. 0
viene valutato false
e 1
valutato true
. Il fatto che usi un'espressione booleana ( 1 == 1
) o 1
, per definire true
, non fa alcuna differenza. Entrambi vengono valutati int
.
Si noti che la libreria standard C fornisce un'intestazione specifica per la definizione booleani: stdbool.h
.
true
viene valutato 1
e false
valutato 0
. C non conosce i tipi booleani nativi, sono solo ints.
int
, con valore 0
o 1
. C ha un tipo booleano effettivo ( _Bool
, con una macro bool
definita in <stdbool.h>
, ma che è stato aggiunto solo in C99, che non ha modificato la semantica degli operatori per utilizzare il nuovo tipo.
_Bool
e <stdbool.h>
ha #define bool _Bool
.
1 == 1
sull'essere valutato come int
. Modificato.
Non conosciamo il valore esatto a cui TRUE è uguale e i compilatori possono avere le proprie definizioni. Quindi, ciò che si privode è usare quello interno del compilatore per definizione. Questo non è sempre necessario se si hanno buone abitudini di programmazione, ma si possono evitare problemi per qualche cattivo stile di programmazione, ad esempio:
if ((a> b) == TRUE)
Questo potrebbe essere un disastro se si definisce TRUE manualmente come 1, mentre il valore interno di TRUE è un altro.
>
operatore restituisce sempre 1 per vero, 0 per falso. Non c'è alcuna possibilità che nessun compilatore C possa sbagliare. Paragoni di uguaglianza TRUE
e FALSE
stile scadente; quanto sopra è più chiaramente scritto come if (a > b)
. Ma l'idea che diversi compilatori C possano trattare la verità e il falso in modo diverso è semplicemente errata.
In genere nel linguaggio di programmazione C, 1 è definito come vero e 0 è definito come falso. Ecco perché vedi quanto segue abbastanza spesso:
#define TRUE 1
#define FALSE 0
Tuttavia, qualsiasi numero diverso da 0 verrebbe valutato come vero anche in un'istruzione condizionale. Pertanto utilizzando quanto segue:
#define TRUE (1==1)
#define FALSE (!TRUE)
Puoi semplicemente dimostrare esplicitamente che stai provando a giocare in sicurezza rendendo falso uguale a tutto ciò che non è vero.
#define TRUE (’/’/’/’)
:;#define FALSE (’-’-’-’)
(tratto da coding-guidelines.com/cbook/cbook1_1.pdf pagina 871)