Używanie wartości boolowskich w C.

Odpowiedzi:

1048

Od najlepszych do gorszych:

Opcja 1 (C99)

#include <stdbool.h>

Opcja 2

typedef enum { false, true } bool;

Opcja 3

typedef int bool;
enum { false, true };

Opcja 4

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

Wyjaśnienie

  • Opcja 1 będzie działać tylko, jeśli używasz C99 i jest to „standardowy sposób”, aby to zrobić. Wybierz to, jeśli to możliwe.
  • Opcje 2, 3 i 4 będą w praktyce miały identyczne zachowanie. # 2 i # 3 nie używają jednak # definicji, co moim zdaniem jest lepsze.

Jeśli nie jesteś zdecydowany, idź z numerem 1!

Thomas Bonini
źródło
1
Czy możesz wyjaśnić, dlaczego są to najlepsze do najgorszych wyborów?
endolith
1
@endolith Wyrównanie, optymalizacje i sposób przechowywania <stdbool.h> boolwybranego kompilatora mogą być bardziej odpowiednie do zamierzonego celu wartości boolowskiej niż wykorzystanie int(tzn. kompilator może wybrać implementację boolinną niż an int). Jeśli masz szczęście, może to również skutkować bardziej rygorystycznym sprawdzaniem typu w czasie kompilacji.
blubberdiblub
1
Dlaczego warto korzystać intz bool? To marnotrawstwo. Zastosowanie unsigned char. Lub użyj polecenie wbudowane c za _Bool.
Bez Ciała
@NoBody Korzystanie z mniejszego typu może zaoszczędzić na pamięci, ale może nie przyspieszyć. Często szybsze jest użycie natywnego rozmiaru słowa procesora zamiast mniejszego, ponieważ może to wymagać od kompilatora zmiany bitów, aby poprawnie go wyrównać
Ted Klein Bergman
240

Kilka przemyśleń na temat booleanów w C:

Jestem wystarczająco stary, że używam zwykłego ints jako mojego typu logicznego bez żadnych typedefs, specjalnych definicji lub wyliczeń dla wartości prawda / fałsz. Jeśli zastosujesz się do mojej poniższej sugestii, aby nigdy nie porównywać stałych stałych boolowskich, wystarczy użyć 0/1, aby zainicjować flagi. Takie podejście można jednak uznać za zbyt reakcyjne w dzisiejszych czasach. W takim przypadku należy zdecydowanie skorzystać, <stdbool.h>ponieważ przynajmniej ma tę zaletę, że jest znormalizowana.

Jakiekolwiek są stałe logiczne, używaj ich tylko do inicjalizacji. Nigdy nie pisz czegoś takiego

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

Zawsze można je zastąpić jaśniejszym

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

Pamiętaj, że można je właściwie odczytać na głos.

Nadaj swoim zmiennym logicznym pozytywne nazwy, tj full Zamiast notfull. Ten ostatni prowadzi do kodu, który jest trudny do odczytania. Porównać

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

z

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

Obie poprzednie pary czytają naturalnie, chociaż !notfulltrudno jest je czytać w obecnej postaci, i stają się znacznie gorsze w bardziej złożonych wyrażeniach boolowskich.

Ogólnie należy unikać argumentów logicznych. Rozważ funkcję zdefiniowaną w ten sposób

void foo(bool option) { ... }

W treści funkcji jest bardzo jasne, co oznacza argument, ponieważ ma wygodną i, miejmy nadzieję, znaczącą nazwę. Ale wyglądają strony z połączeniami

foo(TRUE);
foo(FALSE):

W tym przypadku zasadniczo nie można powiedzieć, co oznacza parametr, nie zawsze patrząc na definicję lub deklarację funkcji, a staje się znacznie gorzej, gdy dodasz jeszcze więcej parametrów boolowskich. Sugeruję albo

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

lub

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

W obu przypadkach wygląda teraz strona połączeń

foo(OPT_ON);
foo(OPT_OFF);

który czytelnik ma przynajmniej szansę na zrozumienie bez pogłębiania definicji foo.

Dale Hagglund
źródło
1
A jak porównać dwie zmienne dla równości? Nigdy nie używaj stałych boolowskich działa świetnie, ale nie rozwiązuje problemu w porównaniu z niestałymi.
Baruch
Wybacz mi, ale nie rozumiem pytania. Czy pytasz, jak porównać dwie zmienne boolowskie pod kątem równości? Jeśli tak, to nie a == bdziała?
Dale Hagglund
5
@ Kenji To, co mówisz, jest prawdą, chociaż uważam, że używanie wartości innych niż jedna jako równoważnika prawdy jest prawie zawsze złym pomysłem. Więc w twoim przykładzie, zakładając to ai blicząc od zera, poleciłbym a > 0 == b > 0zamiast tego. Jeśli nalegasz na wykorzystanie prawdziwości arbitralnych niezerowych wartości, otrzymasz !!varwartość logiczną 0/1 równą var, więc możesz napisać !!a == !!b, chociaż wielu czytelników uzna to za mylące.
Dale Hagglund
3
!a == !bjest również wystarczający do testowania równości, niezerowe stają się zerami, a zera stają się jednością.
ryanpattison
5
@rpattiso Masz rację, oczywiście, ale myślę, że przeczytałbym !!ajako „przekonwertować wartość niebędącą boolowską na jej równoważną wartość prawdy”, podczas gdy przeczytałbym !ajako „logicznie odwróć zmienną boolowską a”. W szczególności szukałem konkretnego powodu, dla którego logiczna inwersja była pożądana.
Dale Hagglund,
74

Oto wersja, której użyłem:

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

Ponieważ fałsz ma tylko jedną wartość, ale logiczna prawda może mieć wiele wartości, ale technika ustawia true na to, co kompilator użyje w przeciwieństwie do fałszu.

To rozwiązuje problem z kodowaniem czegoś, co sprowadzałoby się do tego:

if (true == !false)

Myślę, że wszyscy zgodzilibyśmy się, że nie jest to dobra praktyka, ale jednorazowy koszt wykonania „prawda =! Fałsz” eliminujemy ten problem.

[EDYCJA] W końcu użyłem:

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

aby uniknąć kolizji nazw z innymi schematami, które definiowały trueifalse . Ale koncepcja pozostaje taka sama.

[EDYCJA] Aby wyświetlić konwersję liczby całkowitej na wartość logiczną:

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

Pierwszy (najbardziej odpowiedni)! konwertuje niezerową liczbę całkowitą na 0, a następnie drugą (najbardziej w lewo)! konwertuje 0 na amyfalse wartość. Zostawię to jako ćwiczenie dla czytelnika do konwersji zerowej liczby całkowitej.

[EDYCJA] Moim stylem jest używanie jawnego ustawienia wartości w wyliczeniu, gdy wymagana jest konkretna wartość, nawet jeśli wartość domyślna byłaby taka sama. Przykład: Ponieważ fałsz musi wynosić zero, używam false = 0,raczej niżfalse,

Michael Potter
źródło
5
Również Inną korzyścią wynikającą z korzystania teksty stałe jest integracja IDE - true, falsei boolsą podświetlone w większości IDE, ponieważ są wartości enum i typedef, w przeciwieństwie do #defines, które są rzadko składnia podświetlona.
Ciekawe: ignorując, czy to faktycznie działa, czy jest poprawne C (99+), aby umożliwić wyliczeniu odwoływać się do wcześniejszej wartości w tym samym wyliczeniu ?
@ tgm1024 gcc -ansi -pedantic -Wallnie daje ostrzeżeń, więc ufam gcc; Jeśli to c89zadziała, to i tak powinno działać na dobre c99.
rrr
1
„Ponieważ fałsz ma tylko jedną wartość, ale logiczna prawda może mieć wiele wartości, ale technika ustawia prawdę na to, co kompilator użyje jako przeciwieństwo fałszu”. Operator negacji !może tylko zwracać wartości 0i 1dlatego true = !falsezawsze przypisuje wartość 1. Ta metoda nie zapewnia żadnego dodatkowego bezpieczeństwa typedef enum { false, true } bool;.
user694733,
1
Najwcześniej znalazłem z C90 (6.3.3.3 Unary arytmetyczne operatory): „Wynik logicznego operatora negacji! Wynosi 0, jeśli wartość jego argumentu jest nierówna do 0,1, jeśli wartość jego argumentu jest równa 0. wynik ma typ int. Wyrażenie! E jest równoważne (O == E). " Powinno to obejmować każdy kompilator, który kiedykolwiek twierdził, że obsługuje standard C. Kompilatory można oczywiście legalnie zignorować tę zasadę w przypadkach, gdy nie ma to znaczenia dla obserwowalnego zachowania (jak if(!value)), ale wyjątek ten nie ma zastosowania w tym konkretnym przypadku.
user694733,
30

Najpierw pierwsze. C, tj. ISO / IEC 9899 ma typ boolowski od 19 lat . To o wiele dłużej niż oczekiwana długość kariery programistycznej w C z amatorskimi / akademickimi / zawodowymi częściami połączonymi podczas odwiedzania tego pytania . Mój przewyższa to o zaledwie może 1-2 lata. Oznacza to, że w czasie , gdy przeciętny czytelnik w ogóle nauczył się czegoś o C, C faktycznie miał logiczny typ danych .

Dla typu danych, #include <stdbool.h>i wykorzystania true, falsea bool. Lub nie dołączaj go i używaj _Bool, 1a 0zamiast tego.


Istnieją różne niebezpieczne praktyki promowane w innych odpowiedziach na ten wątek. Zajmę się nimi:

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

Jest to nie-nie, ponieważ przypadkowy czytelnik - który nauczył się C w ciągu tych 19 lat - spodziewałby się, że boolodnosi się do faktycznego bool typu danych i zachowałby się podobnie, ale tak nie jest! Na przykład

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

W przypadku C99 bool/ _Bool, bwartość false iff byłaby arówna zero, a truepoza tym. C11 6.3.1.2p1

  1. Kiedy dowolna wartość skalarna jest konwertowana _Bool, wynikiem jest 0, jeśli wartość jest równa 0; w przeciwnym razie wynik to 1. 59)

Przypisy

59) NaN nie porównują równe 0, a zatem przeliczają na 1.

Z typedefna miejscu, doublebyłby zmuszony do int- jeżeli wartość podwójnej nie mieści się w zakresie do intThe zachowanie jest niezdefiniowane .

Oczywiście to samo dotyczy, jeśli truei falsezostały zadeklarowane w enum.

Jeszcze bardziej niebezpieczne jest deklarowanie

typedef enum bool {
    false, true
} bool;

ponieważ teraz wszystkie wartości oprócz 1 i 0 są nieprawidłowe i gdyby taka wartość została przypisana do zmiennej tego typu, zachowanie byłoby całkowicie niezdefiniowane .

Dlatego jeśli nie możesz użyć C99 z jakiegoś niewytłumaczalnego powodu, dla zmiennych boolowskich powinieneś użyć:

  • typ inti wartości 0i 1 jak jest ; i ostrożnie wykonuj konwersje domen z innych wartości na te z podwójną negacją!!
  • lub jeśli nalegasz , że nie pamiętasz, że 0 to fałsz i niezerowe truish, przynajmniej użyj wielkich liter , tak aby nie dostać mylić z pojęciami C99: BOOL, TRUEi FALSE!
Antti Haapala
źródło
1
Jaka część normy C ograniczyłaby obiekty wyliczonych typów do przechowywania wartości wyraźnie w nich wymienionych? Jeśli największa wartość wyliczonej stałej jest mniejsza niż UCHAR_MAX lub USHRT_MAX, implementacja może użyć typu mniejszego niż intlub unsigned intdo przechowywania wyliczenia, ale nie znam nic w standardzie, co spowodowałoby, że wyliczenie działałoby jak coś innego niż liczba całkowita rodzaj.
supercat
18
typedef enum {
    false = 0,
    true
} t_bool;
mouviciel
źródło
2
Od 2 do MAX_INT również należy ocenić na prawdę
technozaur
2
@technosaurus Takie podejście nie gwarantuje! false == true, ponieważ! false może być dowolną liczbą niezerową. Prostym obejściem byłoby jawne przypisanie wartości true do! False.
Andrew
3
@Andrew To nieprawda. !0 = 1według normy C i !a = 0dla dowolnej niezerowej wartości a. Problem polega na tym, że wszelkie niezerowe są uważane za prawdziwe. Więc jeśli ai boba są „prawdziwe”, niekoniecznie jest tak, że `a == b`.
Jeremy West,
14

C ma typ boolowski: bool (przynajmniej przez ostatnie 10 (!) Lat)

Dołącz stdbool.h, a true / false będzie działać zgodnie z oczekiwaniami.

dmeister
źródło
12
10 lat w standardzie, ale nie 10 lat w kompilatorach! Kompilacja C MSVC ++ w ogóle nie obsługuje C99 poza dopuszczaniem // komentarzy i nigdy nie będzie tego robić. Również _Bool jest zdefiniowany w C99 jako typ wbudowany, podczas gdy bool jest typedef w nagłówku <stdbool.h>.
Clifford,
5
@Clifford 4 lata po twoim komentarzu ... nic się nie zmieniło. MSVC jest kompilatorem C ++ i wierzę, że MS powiedziało, że tak naprawdę nie chce obsługiwać wszystkich nowych funkcji C (C99 i C11). Ale nie mogę przyjąć, że MSVC nie obsługuje nowych funkcji C jako powodu (szczególnie, gdy mówisz to w ciągu 10 lat od odpowiedzi). 10 lat to naprawdę długi czas w świecie programowania. Każdy przyzwoity kompilator powinien mieć wsparcie w ciągu niecałych 10 lat, jeśli sprzedawca zamierza go obsługiwać.
PP
2
@KingsIndian: Nie jestem pewien, dlaczego skierowałeś swój komentarz do mnie, a nawet nie odczułem potrzeby komentowania. Podaję tylko sytuację, jaka była w momencie pisania. Nie popierałem tej sytuacji, wskazując jedynie, że „odpowiedź” może nie mieć zastosowania we wszystkich okolicznościach.
Clifford
@Clifford: Ściśle mówiąc, standard wymaga boolmakra, które rozwija się do _Bool. Różnica jest ważna, ponieważ możesz #undefmakra (i jest to dozwolone, przynajmniej jako środek przejściowy), ale nie możesz wpisać untypedefczcionki. Nie zmienia to jednak głównej myśli pierwszego komentarza.
Jonathan Leffler
2
VS2015 a następnie (i ewentualnie wcześniej, aż do punktu) mają żadnego problemu boolpoprzez <stdbool.h>zgodnie C kompilacji. To rozwiązuje _Bool.
11

Wszystkie niezerowe wartości są sprawdzane jako prawdziwe w operacjach boolowskich, więc możesz po prostu

#define TRUE 1
#define FALSE 0

i użyj stałych.

ggambett
źródło
10
ale używaj ich ostrożnie: ponieważ prawdziwy wynik może być dowolną wartością niezerową, testy if (t == TRUE) {...} i if (t), które są równoważne w innych językach, nie są równoważne w C .
Fortega
1
Masz rację, ale dotyczy to również C ++, które ma typ bool, prawda? Podczas debugowania widziałem zmienne bool o wartości 5837834939 ...
ggambett
1
W C ++ test if (t == true) równa się testowi if (t), ponieważ C ++ dokonuje pewnej konwersji (wszystko, co nie jest równe 0 lub wartość wskaźnika zerowego jest konwertowana na true)
Fortega
6
Wszystko, co powinieneś założyć o prawdziwej wartości logicznej, to to, że jest ona niezerowa. Więc kod jak jeśli (b) jest bezpieczny, podczas gdy if (b == PRAWDA) nie jest; ta ostatnia jest złą praktyką (i bez sensu).
Clifford,
5

Tylko uzupełnienie innych odpowiedzi i wyjaśnienie, jeśli możesz używać 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   |
+-------+----------------+-------------------------+--------------------+

Niektóre z moich preferencji:

  • _Boolczy bool? Oba są w porządku, ale boolwyglądają lepiej niż słowo kluczowe_Bool .
  • Akceptowane wartości dla booli _Boolsą: falselub true. Przypisywanie 0lub 1zamiast falselub truejest poprawne, ale trudniej jest je odczytać i zrozumieć przepływ logiki.

Niektóre informacje ze standardu:

  • _BoolNIE jest unsigned int, ale należy do grup całkowitych bez znaku . Jest wystarczająco duży, aby pomieścić wartości 0lub1 .
  • NIE, ale tak, jesteś w stanie przedefiniować bool trueifalse pewno nie jest to dobry pomysł. Ta zdolność jest uważana za przestarzałą i zostanie usunięta w przyszłości.
  • Przypisanie typu skalarnego (typy arytmetyczne i typy wskaźników) do _Boollub bool, jeśli wartość skalarna jest równa 0lub 0będzie się z nią porównywać 0, w przeciwnym razie wynikiem jest 1: _Bool x = 9; 9jest konwertowany na 1po przypisaniu x.
  • _Boolma 1 bajt (8 bitów), zwykle programiści kuszą, aby spróbować użyć innych bitów, ale nie jest to zalecane, ponieważ jedyną gwarancją jest to, że tylko jeden bit służy do przechowywania danych, a nie jak typ, charktóry ma 8 dostępne bity.
Niezdefiniowane zachowanie
źródło
2

To jest to:

#define TRUE 1
#define FALSE 0
RngTng
źródło
7
Idę z czymś takim jak #define TRUE! FALSE
Tom
2

Możesz użyć do tego znaku char lub innego kontenera o małej liczbie.

Pseudo kod

#define TRUE  1
#define FALSE 0

char bValue = TRUE;
Filip Ekberg
źródło
Również w C jest zwykle liczbą całkowitą i może powodować utratę precyzyjnych ostrzeżeń przez inny kod, który używa liczby wewnętrznej.
Thomas Bonini,
O ile nie optymalizujesz ręcznie pod kątem miejsca, zawsze lepiej jest użyć zwykłego rozmiaru słowa (np. Zwykle an int), ponieważ na niektórych architekturach znaczny spadek wydajności wynika z konieczności rozpakowywania / maskowania tych zmiennych.
Kingsley,
2

Możesz użyć _Bool, ale zwracana wartość musi być liczbą całkowitą (1 dla true, 0 dla false). Zaleca się jednak dołączenie i użycie bool jak w C ++, jak powiedziano w tej odpowiedzi z forum daniweb , a także w tej odpowiedzi , z tego innego pytania dotyczącego przepełnienia stosu :

_Bool: typ logiczny C99. Bezpośrednie korzystanie z _Bool jest zalecane tylko wtedy, gdy utrzymujesz starszy kod, który już definiuje makra dla bool, true lub false. W przeciwnym razie te makra są znormalizowane w nagłówku. Dołącz ten nagłówek, a będziesz mógł używać bool tak samo jak w C ++.

Lokian
źródło
2

Wyrażenia warunkowe są uważane za prawdziwe, jeśli są niezerowe, ale standard C wymaga, aby operatory logiczne same zwracały 0 lub 1.

@Tom: #define TRUE! FALSE jest zły i całkowicie bezcelowy. Jeśli plik nagłówkowy trafia do skompilowanego kodu C ++, może to prowadzić do problemów:

void foo(bool flag);

...

int flag = TRUE;
foo(flag);

Niektóre kompilatory generują ostrzeżenie o konwersji int => bool. Czasami ludzie unikają tego, wykonując:

foo(flag == TRUE);

aby wymusić wyrażenie jako bool C ++. Ale jeśli zdefiniujesz PRAWDA! FAŁSZ, otrzymasz:

foo(flag == !0);

co kończy się porównywaniem wartości całkowitych, które i tak mogą wywołać ostrzeżenie.

jamesdlin
źródło
1

Jeśli używasz C99, możesz użyć tego _Booltypu. Nie #includesą konieczne. Musisz jednak traktować to jak liczbę całkowitą, gdzie 1jest truei 0jest false.

Następnie możesz zdefiniować TRUEi FALSE.

_Bool this_is_a_Boolean_var = 1;


//or using it with true and false
#define TRUE 1
#define FALSE 0
_Bool var = TRUE;
That_Linux_Guy
źródło
Lub możesz #include <stdbool.h>i używasz bool, truei falsetak jak standard tego chce.
SS Anne
1

Obecnie C99 obsługuje typy logiczne, ale musisz #include <stdbool.h>.

Przykład:

#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; 
} 

Wynik:

0
1
Kalana
źródło
0

Oto, czego używam:

enum {false, true};
typedef _Bool bool;

_Bool jest wbudowanym typem C. Jest przeznaczony do wartości boolowskich.

Bez ciała
źródło
-2

Możesz po prostu użyć #definedyrektywy w następujący sposób:

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

I użyj w następujący sposób:

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

i tak dalej

Prakash Bhattarai
źródło
5
NOT makro powinny być chronione przez nawiasy wokół argi ekspresji jako całości #define NOT(arg) (((arg) == TRUE) ? FALSE : TRUE). Lepiej byłoby jednak sprawdzić pod kątem fałszu (da poprawną odpowiedź, nawet jeśli miałoby arg23 zamiast 0 lub 1: #define NOT(arg) (((arg) == FALSE) ? TRUE : FALSE)Ale całe wyrażenie można #define NOT(arg) (!(arg))oczywiście sprowadzić do , co daje ten sam wynik)
Jonathan Leffler