Perché #define TRUE (1 == 1) in una macro booleana C anziché semplicemente come 1?


160

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?


35
E altro #define TRUE (’/’/’/’):; #define FALSE (’-’-’-’)(tratto da coding-guidelines.com/cbook/cbook1_1.pdf pagina 871)
osgx

2
No, è paranoia dall'invisibile ^ Wunderinformed, davvero. In C, 1 e 0 fanno lo stesso in tutte le circostanze.
Jens,

@osgx Cosa significa?
MrGloom,

Risposte:


155

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)


7
Non è corretto, i bool non sono usati qui. Il risultato di 1==1è un int. (vedi stackoverflow.com/questions/7687403/… .)
Mat

4
@Mat: anche in C ++, con il booleantipo?
SLaks

9
La domanda è taggata C, ma in C ++ gli operatori relazionali ritornano trueo false.
Mat

5
@Mat: suppongo che tale codice sia scritto nelle intestazioni C per giocare bene con C ++
SLaks

20
@SLaks Se voleva giocare bene con C ++, lo farebbe #define TRUE truee #define FALSE falseogni volta che __cplusplusviene definito.
Nikos C.

137

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?


6
(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.
Keith Thompson,

1
In realtà il valore restituito da 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.
Maciej Piechotka,

2
@KeithThompson - Forse "portabilità" era il termine sbagliato. Ma resta il fatto che (1 == 1) è un valore booleano; 1 non lo è.
Adam Liss,

2
@AdamLiss: in C, (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?
Keith Thompson,

2
'not' 5 è, in effetti, -6, a livello di bit.
Woliveirajr,

51

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ò:

  • questa pratica di definire TRUEe FALSEcome (0==0)e (1==0)precede C99.
  • ci sono ancora buoni motivi per stare alla larga da C99 e lavorare con C90.

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 ++.


3
Risposta eccellente e non apprezzata. Non è affatto ovvio che le due definizioni di TRUEdifferiranno in C ++.
user4815162342

4
In che modo le funzioni sovraccaricate sono rilevanti per il codice che viene compilato come C e C ++?
Keith Thompson,

@KeithThompson Non si tratta solo di sovraccarico, ma di una corretta digitazione in generale, il sovraccarico è solo l'esempio più pratico quando entra in gioco. Ovviamente il codice C ++ senza sovraccarichi, modelli e tutte quelle cose "complicate" rimosse per la "compatibilità C" non si preoccupa molto dei tipi, ma ciò non significa che si dovrebbero rovesciare le limitazioni concettuali dei tipi in un determinato linguaggio .
Christian Rau,

1
@ChristianRau: Cosa intendi con "non importa molto dei tipi"? I tipi sono centrali nel linguaggio C; ogni espressione, valore e oggetto in un programma C ha un tipo ben definito. Se vuoi definire qualcosa di diverso in C e in C ++ (nei rari casi in cui devi effettivamente scrivere codice che compila sia in C che in C ++), puoi usare #ifdef __cplusplusper esprimere le tue intenzioni in modo molto più chiaro.
Keith Thompson,

@KeithThompson Sì, so quanto siano importanti i tipi. È solo che senza tutte le cose attente al tipo come il sovraccarico e i modelli, cose come la differenziazione tra 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.
Christian Rau,

18
#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 come 1. 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
Ho un dubbio, TRUE viene sostituito ad ogni utilizzo con 1 == 1, mentre solo l'utilizzo di 1 sostituirà 1, non è il primo metodo di confronto extra in termini di sovraccarico ... o è realizzato un compilatore ottimizzato?
pinkpanther,

4
Le espressioni costanti di @pinkpanther sono generalmente valutate al momento della compilazione e quindi non inducono alcun sovraccarico.
ouah,

2
1==1è garantito per essere valutato a1
ouah,

3
@NikosC. è una buona domanda. Questo è importante per il codice del modulo if(foo == true), che andrà dalla semplice cattiva pratica al buggy a tutto campo.
Djechlin,

1
+1 per indicare i pericoli in (x == TRUE)può avere un valore di verità diverso da x.
Joshua Taylor,

12

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.


1
Questo è un buon punto, e forse alcuni di questi strumenti possono persino catturare codice stupido 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>).
Kaz,

4

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.


certo che non l'hai fatto ... ma alcune persone potrebbero pensare diversamente, specialmente per i numeri negativi, ecco perché :)
pinkpanther,

Che cosa? Lo hai al contrario. trueviene valutato 1e falsevalutato 0. C non conosce i tipi booleani nativi, sono solo ints.
Djechlin,

In C, gli operatori relazionali e di uguaglianza producono risultati di tipo 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.
Keith Thompson,

@djechlin: A partire dalla serie 1999 C ha un tipo booleano nativo. Si chiama _Boole <stdbool.h>ha #define bool _Bool.
Keith Thompson,

@KeithThompson, hai ragione 1 == 1sull'essere valutato come int. Modificato.
Scarpa

3

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.


In C, l' >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.
Keith Thompson,

2
  1. Voce di elenco

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.


4
Non lo definirei "giocare sul sicuro" - piuttosto, ti stai dando un falso senso di sicurezza.
dodgethesteamroller,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.