Utilizzando valori booleani in C


Risposte:


1049

Dal meglio al peggio:

Opzione 1 (C99)

#include <stdbool.h>

opzione 2

typedef enum { false, true } bool;

Opzione 3

typedef int bool;
enum { false, true };

Opzione 4

typedef int bool;
#define true 1
#define false 0

Spiegazione

  • L'opzione 1 funzionerà solo se usi C99 ed è il "modo standard" per farlo. Scegli questo se possibile.
  • Le opzioni 2, 3 e 4 avranno in pratica lo stesso identico comportamento. # 2 e # 3 non usano #define, il che secondo me è meglio.

Se sei indeciso, vai con # 1!


1
Puoi spiegarci perché sono le scelte migliori da quelle peggiori?
endolith

1
@endolith L'allineamento, le ottimizzazioni e il modo di memorizzare un <stdbool.h> boolcompilatore scelto possono essere più adatti allo scopo previsto di un valore booleano che all'utilizzo di un int(ovvero il compilatore può scegliere di implementare un metodo booldiverso da un int). Se sei fortunato, potresti anche verificare un tipo più rigoroso al momento della compilazione.
blubberdiblub

1
Perché usare intper bool? Questo è uno spreco. Usa unsigned char. Oppure utilizzare integrato di C _Bool.
user12211554

@NoBody L'uso di un tipo più piccolo può far risparmiare memoria, ma potrebbe non renderlo più veloce. Spesso, è più veloce usare la dimensione della parola nativa del processore invece di una dimensione più piccola in quanto potrebbe richiedere al compilatore di effettuare spostamenti di bit per allinearlo correttamente
Ted Klein Bergman

241

Alcuni pensieri sui booleani in C:

Sono abbastanza vecchio che uso semplicemente ints come tipo booleano senza typedef o definizioni o enumerazioni speciali per valori true / false. Se segui il mio suggerimento di seguito sul non confrontare mai le costanti booleane, allora devi solo usare 0/1 per inizializzare i flag. Tuttavia, un simile approccio può essere considerato troppo reazionario in questi tempi moderni. In tal caso, si dovrebbe assolutamente usare <stdbool.h>poiché almeno ha il vantaggio di essere standardizzato.

Qualunque siano le costanti booleane chiamate, usale solo per l'inizializzazione. Mai e poi mai scrivere qualcosa del genere

if (ready == TRUE) ...
while (empty == FALSE) ...

Questi possono sempre essere sostituiti dal più chiaro

if (ready) ...
while (!empty) ...

Si noti che questi possono effettivamente essere ragionevolmente e comprensibilmente letti ad alta voce.

Dai un nome positivo alle tue variabili booleane, ovvero fullinvece di notfull. Quest'ultimo porta a un codice che è difficile da leggere facilmente. Confrontare

if (full) ...
if (!full) ...

con

if (!notfull) ...
if (notfull) ...

Entrambe le prime coppie leggono naturalmente, mentre !notfullè scomodo da leggere così com'è, e peggiora molto nelle espressioni booleane più complesse.

Argomenti booleani dovrebbero generalmente essere evitati. Considera una funzione definita come questa

void foo(bool option) { ... }

All'interno del corpo della funzione, è molto chiaro cosa significhi l'argomento poiché ha un nome conveniente e, si spera, significativo. Ma i siti di chiamata sembrano

foo(TRUE);
foo(FALSE):

Qui, è essenzialmente impossibile dire cosa significasse il parametro senza sempre guardare la definizione o la dichiarazione della funzione, e peggiora molto non appena si aggiungono ancora più parametri booleani. Lo consiglio anche io

typedef enum { OPT_ON, OPT_OFF } foo_option;
void foo(foo_option option);

o

#define OPT_ON true
#define OPT_OFF false
void foo(bool option) { ... }

In entrambi i casi, il sito di chiamata ora sembra

foo(OPT_ON);
foo(OPT_OFF);

che il lettore ha almeno una possibilità di comprendere senza trascinare la definizione di foo.


1
E come si confrontano due variabili per l'uguaglianza? Non usare mai le costanti booleane funziona alla grande, ma non risolve il problema se confrontato con una non costante.
Baruch,

Perdonami, ma non capisco la domanda. Mi stai chiedendo come confrontare due variabili booleane per l'uguaglianza? In tal caso, non a == bfunziona?
Dale Hagglund,

5
@Kenji Quello che dici è vero, anche se credo che usare valori diversi da uno come equivalente per true sia quasi sempre una cattiva idea. Quindi nel tuo esempio, supponendo che ae bcontando da zero, raccomanderei a > 0 == b > 0invece. Se insisti nel trarre vantaggio dalla veridicità di valori arbitrari diversi da zero, !!varil valore booleano 0/1 equivale a var, quindi potresti scrivere !!a == !!b, anche se molti lettori lo troveranno confuso.
Dale Hagglund,

3
!a == !bè anche sufficiente per testare l'uguaglianza, i non zeri diventano zero e gli zeri diventano uno.
ryanpattison,

5
@rpattiso Hai perfettamente ragione, ovviamente, ma immagino che leggerei !!a"converti una non booleana in un valore di verità equivalente", mentre leggo !acome "inverti logicamente la variabile booleana a". In particolare, avrei cercato un motivo specifico per cui si desiderava l'inversione logica.
Dale Hagglund, l'


74

Ecco la versione che ho usato:

typedef enum { false = 0, true = !false } bool;

Perché false ha un solo valore, ma un true logico potrebbe avere molti valori, ma la tecnica imposta true per essere ciò che il compilatore utilizzerà per l'opposto di false.

Questo risolve il problema di qualcuno che codifica qualcosa che verrebbe a questo:

if (true == !false)

Penso che saremmo tutti d'accordo sul fatto che questa non è una buona pratica, ma per il costo di una volta di fare "true =! False" eliminiamo quel problema.

[EDIT] Alla fine ho usato:

typedef enum { myfalse = 0, mytrue = !myfalse } mybool;

per evitare la collisione del nome con altri schemi che stavano definendo truee false. Ma il concetto rimane lo stesso.

[EDIT] Per mostrare la conversione di numero intero in booleano:

mybool somebool;
int someint = 5;
somebool = !!someint;

Il primo (proprio quello più)! converte il numero intero diverso da zero in uno 0, quindi il secondo (più a sinistra)! converte lo 0 in un myfalsevalore. Lascio che sia un esercizio per il lettore convertire un numero intero zero.

[EDIT] Il mio stile è usare l'impostazione esplicita di un valore in un enum quando il valore specifico è richiesto anche se il valore predefinito sarebbe lo stesso. Esempio: poiché falso deve essere zero, utilizzo false = 0,anzichéfalse,


5
Anche un altro vantaggio di utilizzare le enumerazioni è l'integrazione IDE - true, falsee boolsono evidenziate in più di quanto sono valori enum e typedef, al contrario di IDE #defines, che sono raramente sintassi evidenziata.

Curioso: ignorando se funziona davvero, è valido C (99+) per consentire a un enum di fare riferimento a un valore precedente nella stessa enumerazione ?

@ tgm1024 gcc -ansi -pedantic -Wallnon dà avvisi, quindi mi fido gcc; Se questo funziona anche c89perché dovrebbe funzionare anche per c99.
yyny,

1
"Perché false ha un solo valore, ma un true logico può avere molti valori, ma la tecnica imposta true per essere ciò che il compilatore utilizzerà per il contrario di false." L'operatore di negazione !può solo restituire valori 0e 1, quindi true = !false, assegnerà sempre il valore 1. Questo metodo non fornisce alcuna sicurezza aggiuntiva typedef enum { false, true } bool;.
user694733

1
Il primo che ho trovato proviene da C90 (6.3.3.3 Operatori aritmetici unari): "Il risultato dell'operatore di negazione logica! È 0 se il valore del suo operando confronta in modo non uguale a 0. 1 se il valore del suo operando è uguale a 0. Il risultato ha tipo int. L'espressione! E è equivalente a (O == E). " Ciò dovrebbe riguardare qualsiasi compilatore che abbia mai sostenuto di supportare lo standard C. I compilatori possono ovviamente ignorare legalmente questa regola nei casi in cui non ha importanza per il comportamento osservabile (come if(!value)), ma tale eccezione non è applicabile in questo caso specifico.
user694733


30

Cominciando dall'inizio. C, ovvero ISO / IEC 9899 ha avuto un tipo booleano da 19 anni . Questo è un tempo molto più lungo della durata prevista della carriera di programmazione C con parti amatoriali / accademiche / professionali combinate quando si visita questa domanda . Il mio lo supera di appena 1-2 anni. Significa che durante il tempo in cui un lettore medio ha imparato qualcosa su C, C ha effettivamente avuto il tipo di dati booleani .

Per il tipo di dati, #include <stdbool.h>e use true, falsee bool. O non lo si includa, e l'uso _Bool, 1e 0invece.


Ci sono varie pratiche pericolose promosse nelle altre risposte a questo thread. Li affronterò:

typedef int bool;
#define true 1
#define false 0

Questo è un no-no, perché un lettore occasionale - che ha imparato C in questi 19 anni - si aspetterebbe che si boolriferisca al tipo di dati effettivo bool e si comporterebbe in modo simile, ma non lo fa! Per esempio

double a = ...;
bool b = a;

Con C99 bool/ _Bool, bsarebbe impostato su false iff a era zero e in caso truecontrario. C11 6.3.1.2p1

  1. Quando un valore scalare viene convertito in _Bool, il risultato è 0 se il valore confronta uguale a 0; altrimenti, il risultato è 1. 59)

Le note

59) NaNs non confronta uguale a 0 e quindi converte in 1.

Con il typedefposto, il doublesarebbe costretto a un int- se il valore del doppio non è nell'intervallo per int, il comportamento non è definito .

Lo stesso vale naturalmente per se truee falsefosse stato dichiarato in un enum.

Ciò che è ancora più pericoloso è dichiarare

typedef enum bool {
    false, true
} bool;

poiché ora tutti i valori oltre a 1 e 0 non sono validi e se tale valore fosse assegnato a una variabile di quel tipo, il comportamento sarebbe del tutto indefinito .

Pertanto, se non è possibile utilizzare C99 per qualche motivo inspiegabile, per le variabili booleane è necessario utilizzare:

  • tipo inte valori 0e così 1 come sono ; e fare attentamente conversioni di dominio da qualsiasi altro valore a questi con doppia negazione!!
  • o se insistete non si ricorda che 0 è falsy e diverso da zero truish, almeno l'uso maiuscola in modo che non vengano confusi con i concetti C99: BOOL, TRUEe FALSE!

1
Quale parte della norma C limiterebbe gli oggetti di tipi enumerati a contenere i valori esplicitamente elencati in essa? Se il valore più grande per una costante enumerata è inferiore a UCHAR_MAX o USHRT_MAX, un'implementazione potrebbe utilizzare un tipo più piccolo di into unsigned intper contenere un'enumerazione, ma non so nulla nello Standard che provocherebbe un comportamento di un'enumerazione come qualsiasi cosa diversa da un numero intero genere.
supercat

18
typedef enum {
    false = 0,
    true
} t_bool;

2
2 fino a MAX_INT dovrebbe valutare anche true
technosaurus

2
@technosaurus Questo approccio non garantisce! false == true poiché! false può essere qualsiasi numero diverso da zero. Una semplice soluzione sarebbe quella di assegnare esplicitamente true a! False.
Andrew

3
@Andrew Non è vero. !0 = 1dallo standard C e !a = 0per qualsiasi valore diverso da zero di a. Il problema è che qualsiasi diverso da zero è considerato vero. Quindi se ae bsono entrambi "veri", non è necessariamente il caso che `a == b`.
Jeremy West,

14

C ha un tipo booleano: bool (almeno per gli ultimi 10 (!) Anni)

Includi stdbool.h e true / false funzionerà come previsto.


12
10 anni nello standard, ma non 10 anni nei compilatori! La compilazione C di MSVC ++ non supporta affatto C99 oltre a consentire // commenti e non è probabile che lo faccia. Anche _Bool è definito in C99 come tipo incorporato, mentre bool è un typedef nell'intestazione <stdbool.h>.
Clifford,

5
@Clifford 4 anni dopo il tuo commento ... nulla è cambiato. MSVC è un compilatore C ++ e credo che MS abbia affermato di non voler veramente supportare tutte le nuove funzionalità C (C99 e C11). Ma non posso supporre che MSVC non supporti le nuove funzionalità C come motivo (specialmente quando lo dici contro un decennio di risposta). 10 anni sono davvero tanti nel mondo della programmazione. Qualsiasi compilatore decente dovrebbe avere il supporto per esso in molto meno di 10 anni se il fornitore ha intenzione di supportarlo.
PP,

2
@KingsIndian: Non sono sicuro del motivo per cui mi hai indirizzato il tuo commento o addirittura sentito il bisogno di commentare. Stavo solo affermando la situazione così com'era al momento della scrittura. Non stavo sostenendo quella situazione, ma semplicemente sottolineando che la "risposta" potrebbe non essere applicabile in tutte le circostanze.
Clifford,

@Clifford: rigorosamente, lo standard richiede booldi essere una macro che si espande _Bool. La differenza conta perché puoi #undefuna macro (e questo è permesso, almeno come misura transitoria), ma non puoi untypedefscrivere un typedef. Tuttavia, non altera la spinta principale del tuo primo commento.
Jonathan Leffler,

2
VS2015 e più tardi (e forse anche prima, fino a un certo punto) non hanno alcun problema con boolattraverso <stdbool.h>sotto C compilazione. Si risolve a _Bool.

11

Qualsiasi cosa diversa da zero viene valutata come vera nelle operazioni booleane, quindi potresti semplicemente

#define TRUE 1
#define FALSE 0

e usa le costanti.


10
ma usali con cura: poiché un risultato vero può essere qualsiasi valore diverso da zero, i test if (t == TRUE) {...} e if (t), che sono equivalenti in altre lingue, non sono equivalenti in C .
Fortega

1
Hai ragione, ma questo è vero anche in C ++ che ha un tipo bool, giusto? Durante il debug ho visto variabili bool con valori di 5837834939 ...
ggambett,

1
In C ++, il test if (t == true) è uguale al test if (t), poiché C ++ esegue una conversione (tutto ciò che non è 0 o un valore di puntatore nullo viene convertito in vero)
Fortega

6
Tutto ciò che dovresti assumere su un valore vero booleano è che è diverso da zero. Quindi codice come if (b) è sicuro mentre if (b == TRUE) non lo è; quest'ultima è una cattiva pratica (e inutile).
Clifford,

5

Solo un complemento ad altre risposte e alcuni chiarimenti, se ti è permesso usare C99.

+-------+----------------+-------------------------+--------------------+
|  Name | Characteristic | Dependence in stdbool.h |        Value       |
+-------+----------------+-------------------------+--------------------+
| _Bool |   Native type  |    Don't need header    |                    |
+-------+----------------+-------------------------+--------------------+
|  bool |      Macro     |           Yes           | Translate to _Bool |
+-------+----------------+-------------------------+--------------------+
|  true |      Macro     |           Yes           |   Translate to 1   |
+-------+----------------+-------------------------+--------------------+
| false |      Macro     |           Yes           |   Translate to 0   |
+-------+----------------+-------------------------+--------------------+

Alcune delle mie preferenze:

  • _Boolo bool? Entrambi vanno bene, ma boolsembrano migliori della parola chiave _Bool.
  • I valori accettati per boole _Boolsono: falseo true. L'assegnazione 0o 1invece di falseo trueè valida, ma è più difficile da leggere e comprendere il flusso logico.

Alcune informazioni dallo standard:

  • _Boolnon è unsigned int, ma fa parte del gruppo di tipi interi senza segno . È abbastanza grande da contenere i valori 0o 1.
  • DO NOT, ma sì, si è in grado di ridefinire bool truee falsema di sicuro non è una buona idea. Questa capacità è considerata obsoleta e verrà rimossa in futuro.
  • L'assegnazione di un tipo scalare (tipi aritmetici e tipi di puntatore) a _Boolo bool, se il valore scalare è uguale 0o confronta ad 0esso sarà 0, altrimenti il ​​risultato è 1: _Bool x = 9; 9viene convertito in 1quando assegnato a x.
  • _Boolè 1 byte (8 bit), in genere il programmatore è tentato di provare a utilizzare gli altri bit, ma non è raccomandato, poiché l'unico dato garantito è che viene utilizzato solo un bit per memorizzare i dati, non come un tipo charche ha 8 bit disponibili.

2

È questo:

#define TRUE 1
#define FALSE 0

7
Ci andrò con qualcosa come #define TRUE! FALSE
Tom,

2

È possibile utilizzare un carattere o un altro contenitore di piccoli numeri per esso.

Pseudo-codice

#define TRUE  1
#define FALSE 0

char bValue = TRUE;

Anche in C di solito è un int e può causare la perdita di avvisi di precisione da parte di altri codici che usano int ..
Thomas Bonini,

A meno che non si stia ottimizzando a mano per lo spazio, è sempre meglio utilizzare la normale dimensione delle parole dell'hardware (ad esempio: di solito un int), poiché su alcune architetture si ottiene un notevole impatto sulle prestazioni dovendo decomprimere / mascherare i controlli su queste variabili.
Kingsley,

2

È possibile utilizzare _Bool, ma il valore restituito deve essere un numero intero (1 per true, 0 per false). Tuttavia, si consiglia di includere e usare bool come in C ++, come detto in questa risposta dal forum daniweb , così come questa risposta , da questa altra domanda di StackOverflow:

_Bool: tipo booleano di C99. L'uso di _Bool direttamente è consigliato solo se si mantiene un codice legacy che definisce già le macro per bool, true o false. Altrimenti, quelle macro sono standardizzate nell'intestazione. Includi quell'intestazione e puoi usare bool come faresti in C ++.


2

Le espressioni condizionali sono considerate vere se sono diverse da zero, ma lo standard C richiede che gli operatori logici stessi restituiscano 0 o 1.

@ Tom: #define VERO! FALSO è cattivo ed è completamente inutile. Se il file di intestazione si fa strada nel codice C ++ compilato, può causare problemi:

void foo(bool flag);

...

int flag = TRUE;
foo(flag);

Alcuni compilatori genereranno un avviso sulla conversione int => bool. A volte le persone lo evitano facendo:

foo(flag == TRUE);

per forzare l'espressione a essere un bool C ++. Ma se #define TRUE! FALSE, si finisce con:

foo(flag == !0);

che finisce per fare un confronto int-to-bool che può comunque attivare l'avvertimento.


1

Se si utilizza C99, è possibile utilizzare il _Booltipo. Non #includesono necessari. Tuttavia, 1è necessario trattarlo come un numero intero, dove si trova trueed 0è false.

È quindi possibile definire TRUEe FALSE.

_Bool this_is_a_Boolean_var = 1;


//or using it with true and false
#define TRUE 1
#define FALSE 0
_Bool var = TRUE;

Oppure puoi #include <stdbool.h>e usa bool, truee falsecome vuole lo standard.
SS Anne,

1

Oggi C99 supporta tipi booleani ma è necessario #include <stdbool.h>.

Esempio:

#include <stdbool.h>

int main() 
{ 
    bool arr[2] = {true, false}; 

    printf("%d\n", arr[0] && arr[1]);
    printf("%d\n", arr[0] || arr[1]);

    return 0; 
} 

Produzione:

0
1

0

Questo è quello che uso:

enum {false, true};
typedef _Bool bool;

_Bool è un tipo incorporato in C. È inteso per valori booleani.


-2

Puoi semplicemente usare la #definedirettiva come segue:

#define TRUE 1
#define FALSE 0
#define NOT(arg) (arg == TRUE)? FALSE : TRUE
typedef int bool;

E utilizzare come segue:

bool isVisible = FALSE;
bool isWorking = TRUE;
isVisible = NOT(isVisible);

e così via


5
Il NON macro dovrebbero essere protetti dalla parentesi in tutto il arge l'espressione nel suo complesso: #define NOT(arg) (((arg) == TRUE) ? FALSE : TRUE). Tuttavia, sarebbe meglio verificare la falsità (darà la risposta corretta anche se argfosse 23 invece di 0 o 1:. #define NOT(arg) (((arg) == FALSE) ? TRUE : FALSE)Ma l'intera espressione può essere ridotta a #define NOT(arg) (!(arg)), ovviamente, che produce lo stesso risultato.
Jonathan Leffler
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.