C nie ma żadnych wbudowanych typów boolowskich. Jaki jest najlepszy sposób na użycie ich w C?
C nie ma żadnych wbudowanych typów boolowskich. Jaki jest najlepszy sposób na użycie ich w C?
Odpowiedzi:
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
Jeśli nie jesteś zdecydowany, idź z numerem 1!
<stdbool.h>
bool
wybranego kompilatora mogą być bardziej odpowiednie do zamierzonego celu wartości boolowskiej niż wykorzystanie int
(tzn. kompilator może wybrać implementację bool
inną niż an int
). Jeśli masz szczęście, może to również skutkować bardziej rygorystycznym sprawdzaniem typu w czasie kompilacji.
int
z bool
? To marnotrawstwo. Zastosowanie unsigned char
. Lub użyj polecenie wbudowane c za _Bool
.
Kilka przemyśleń na temat booleanów w C:
Jestem wystarczająco stary, że używam zwykłego int
s 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ż !notfull
trudno 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
.
a == b
działa?
a
i b
licząc od zera, poleciłbym a > 0 == b > 0
zamiast tego. Jeśli nalegasz na wykorzystanie prawdziwości arbitralnych niezerowych wartości, otrzymasz !!var
wartość logiczną 0/1 równą var
, więc możesz napisać !!a == !!b
, chociaż wielu czytelników uzna to za mylące.
!a == !b
jest również wystarczający do testowania równości, niezerowe stają się zerami, a zera stają się jednością.
!!a
jako „przekonwertować wartość niebędącą boolowską na jej równoważną wartość prawdy”, podczas gdy przeczytałbym !a
jako „logicznie odwróć zmienną boolowską a”. W szczególności szukałem konkretnego powodu, dla którego logiczna inwersja była pożądana.
Wartość logiczna w C jest liczbą całkowitą: zero dla wartości false i niezerowa dla wartości true.
Zobacz także typ danych logicznych , sekcja C, C ++, Objective-C, AWK .
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 true
ifalse
. 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,
true
, false
i bool
są 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.
gcc -ansi -pedantic -Wall
nie daje ostrzeżeń, więc ufam gcc
; Jeśli to c89
zadziała, to i tak powinno działać na dobre c99
.
!
może tylko zwracać wartości 0
i 1
dlatego true = !false
zawsze przypisuje wartość 1. Ta metoda nie zapewnia żadnego dodatkowego bezpieczeństwa typedef enum { false, true } bool;
.
if(!value)
), ale wyjątek ten nie ma zastosowania w tym konkretnym przypadku.
Jeśli używasz kompilatora C99, ma on wbudowaną obsługę typów bool:
#include <stdbool.h>
int main()
{
bool b = false;
b = true;
}
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
, false
a bool
. Lub nie dołączaj go i używaj _Bool
, 1
a 0
zamiast 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 bool
odnosi 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
, b
wartość false
iff byłaby a
równa zero, a true
poza tym. C11 6.3.1.2p1
- 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 typedef
na miejscu, double
byłby zmuszony do int
- jeżeli wartość podwójnej nie mieści się w zakresie do int
The zachowanie jest niezdefiniowane .
Oczywiście to samo dotyczy, jeśli true
i false
został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ć:
int
i wartości 0
i 1
jak jest ; i ostrożnie wykonuj konwersje domen z innych wartości na te z podwójną negacją!!
BOOL
, TRUE
i FALSE
!int
lub unsigned int
do przechowywania wyliczenia, ale nie znam nic w standardzie, co spowodowałoby, że wyliczenie działałoby jak coś innego niż liczba całkowita rodzaj.
typedef enum {
false = 0,
true
} t_bool;
!0 = 1
według normy C i !a = 0
dla dowolnej niezerowej wartości a
. Problem polega na tym, że wszelkie niezerowe są uważane za prawdziwe. Więc jeśli a
i b
oba są „prawdziwe”, niekoniecznie jest tak, że `a == b`.
C ma typ boolowski: bool (przynajmniej przez ostatnie 10 (!) Lat)
Dołącz stdbool.h, a true / false będzie działać zgodnie z oczekiwaniami.
bool
makra, które rozwija się do _Bool
. Różnica jest ważna, ponieważ możesz #undef
makra (i jest to dozwolone, przynajmniej jako środek przejściowy), ale nie możesz wpisać untypedef
czcionki. Nie zmienia to jednak głównej myśli pierwszego komentarza.
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.
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:
_Bool
czy bool
? Oba są w porządku, ale bool
wyglądają lepiej niż słowo kluczowe_Bool
.bool
i _Bool
są: false
lub true
. Przypisywanie 0
lub 1
zamiast false
lub true
jest poprawne, ale trudniej jest je odczytać i zrozumieć przepływ logiki.Niektóre informacje ze standardu:
_Bool
NIE jest unsigned int
, ale należy do grup całkowitych bez znaku . Jest wystarczająco duży, aby pomieścić wartości 0
lub1
.bool
true
ifalse
pewno nie jest to dobry pomysł. Ta zdolność jest uważana za przestarzałą i zostanie usunięta w przyszłości._Bool
lub bool
, jeśli wartość skalarna jest równa 0
lub 0
będzie się z nią porównywać 0
, w przeciwnym razie wynikiem jest 1
: _Bool x = 9;
9
jest konwertowany na 1
po przypisaniu x
._Bool
ma 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, char
który ma 8 dostępne bity.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;
int
), ponieważ na niektórych architekturach znaczny spadek wydajności wynika z konieczności rozpakowywania / maskowania tych zmiennych.
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 ++.
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.
Jeśli używasz C99, możesz użyć tego _Bool
typu. Nie #include
są konieczne. Musisz jednak traktować to jak liczbę całkowitą, gdzie 1
jest true
i 0
jest false
.
Następnie możesz zdefiniować TRUE
i FALSE
.
_Bool this_is_a_Boolean_var = 1;
//or using it with true and false
#define TRUE 1
#define FALSE 0
_Bool var = TRUE;
#include <stdbool.h>
i używasz bool
, true
i false
tak jak standard tego chce.
Możesz po prostu użyć #define
dyrektywy 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
arg
i 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 arg
23 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)