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 booleantipo effettivo (e risolverà in truee false) se il compilatore lo supporta. (in particolare, C ++)
Tuttavia, sarebbe meglio verificare se C ++ è in uso (tramite la __cplusplusmacro) e effettivamente utilizzare truee false.
In un compilatore C, questo equivale a 0e 1.
(notare che la rimozione delle parentesi interromperà ciò a causa dell'ordine delle operazioni)
1==1è un int. (vedi stackoverflow.com/questions/7687403/… .)
booleantipo?
trueo false.
#define TRUE truee #define FALSE falseogni volta che __cplusplusviene definito.
La risposta è portabilità. I valori numerici TRUEe FALSEnon 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 1e (1 > 2)valuta 0, così come altri hanno detto, non c'è differenza pratica per quanto riguarda il compilatore. Ma lasciando che il compilatore definisca TRUEe FALSEsecondo 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 FALSEcome 0e TRUEcome -1. Come molti linguaggi moderni, interpretavano qualsiasi valore diverso da zero TRUE, ma valutavano espressioni booleane come vere -1. La loro NOToperazione è 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 5valuta TRUE, ma NOT 5valuta -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 FALSEe qualsiasi valore diverso da zero viene interpretato come TRUE, non si dovrebbe mai confrontare espressioni dall'aspetto booleano con TRUEoFALSE . 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 ints come bools. 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 TRUEsolo 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) == TRUEpoi, a > bma l'implicazione inversa potrebbe non essere valida.
(1==1)e 1sono entrambe espressioni costanti di tipo intcon 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 TRUEin 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 == 1ha esattamente lo stesso significato di 1; e 1 == 0ha lo stesso significato di 0. Tuttavia, nelle unità di traduzione C ++, 1 == 1ha tipo bool. Quindi la TRUEmacro definita in questo modo si integra meglio in C ++.
Un esempio di come si integra meglio è che, ad esempio, se la funzione fooha sovraccarichi per inte per bool, allora foo(TRUE)sceglierà il boolsovraccarico. Se TRUEè appena definito come 1, allora non funzionerà bene nel C ++. foo(TRUE)vorrà il intsovraccarico.
Naturalmente, C99 ha introdotto bool, truee falsee questi possono essere utilizzati nei file di intestazione che funzionano con C99 e con C.
Però:
TRUEe FALSEcome (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, falsee boolinvece.
#ifndef __cplusplus
typedef int bool;
#define true (0==0)
#define false (!true)
#endif
Detto questo, il 0==0trucco è 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 truecostante sia boolcompilata 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 boolparametro. 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 ++.
TRUEdifferiranno in C ++.
#ifdef __cplusplusper esprimere le tue intenzioni in modo molto più chiaro.
boole intnon 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 è 0o 1. 1==1è garantito per essere valutato 1ed !(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 TRUEe FALSEmacro:
Per C,
TRUEdovrebbe essere definito come1. Tuttavia, altre lingue usano quantità diverse da 1, quindi alcuni programmatori ritengono che lo!0stia giocando in modo sicuro.
Sempre in C99, le stdbool.hdefinizioni per le macro booleane truee 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. 0viene valutato falsee 1valutato 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.
trueviene valutato 1e falsevalutato 0. C non conosce i tipi booleani nativi, sono solo ints.
int, con valore 0o 1. C ha un tipo booleano effettivo ( _Bool, con una macro booldefinita in <stdbool.h>, ma che è stato aggiunto solo in C99, che non ha modificato la semantica degli operatori per utilizzare il nuovo tipo.
_Boole <stdbool.h>ha #define bool _Bool.
1 == 1sull'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 TRUEe FALSEstile 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)