Czy standard C wyraźnie wskazuje wartość prawdy na 0 lub 1?

86

Wiemy, że wszystkie liczby, które nie są równe, 0są traktowane jak truew C, więc możemy napisać:

int a = 16;

while (a--)
    printf("%d\n", a);  // prints numbers from 15 to 0

Jednak zastanawiałem się, czy prawda / fałsz są zdefiniowane jako 1/ 0w C, więc wypróbowałem poniższy kod:

printf("True = %d, False = %d\n", (0 == 0), (0 != 0));  // prints: True = 1, False = 0

Czy standard C wyraźnie wskazuje wartości prawdy prawda i fałsz odpowiednio jako 1i 0?

Kevin Dong
źródło
3
Myślę, że to SO Pytanie jest istotne
Imran Ali
3
Próbuję uruchomić to pod gccze -std=c89i to daje taki sam wynik.
Kevin Dong
1
@Blackhole, od 15 do 0.
Arturo Torres Sánchez
6
Prawie fałszywy, ale ponad dekadę przed SO / SE: c-faq.com/bool/bool2.html .
dave_thompson_085
1
To, że fałsz to zero, jest kanonem, jednak prawda jest ogólnie uważana za „niezerową”. Ponieważ jednak programiści są tym, czym są, wszyscy używaliśmy 1 jako „niezerowego” z różnych powodów. Zachęcamy Cię do tego, aby nie ufać, że prawda jest równa dokładnie 1. Podczas gdy (0 == 0) jest jednym w Twoim przykładzie, coś takiego jak (12 == 12) mogłoby równie łatwo mieć wartość 12; też prawda".
Inżynier

Odpowiedzi:

96

Czy standard C wyraźnie wskazuje wartości prawdy odpowiednio truei falseas 0i 1?

Standard C definiuje truei falsejako makra, w stdbool.hktórych rozszerzają się odpowiednio do 1i 0.

C11-§7.18:

Pozostałe trzy makra są odpowiednie do użycia w #ifdyrektywach przetwarzania wstępnego. Oni są

true

która rozwija się do stałej liczby całkowitej 1,

false

która jest interpretowana jako stała całkowita 0[...]

Dla operatorów ==i !=, mówi norma

C11-§6.5.9 / 3:

Operatory ==(równe) i !=(nie równe) są analogiczne do operatorów relacyjnych, z wyjątkiem ich niższego priorytetu. 108) Każdy z operatorów daje wynik, 1jeśli określona relacja jest prawdziwa, a 0jeśli jest fałszem. Wynik ma typ int. Dla dowolnej pary operandów dokładnie jedna z relacji jest prawdziwa.

haccks
źródło
20
Wydaje mi się, że pytanie dotyczy 0 == 0i 0 != 0itp., A nie wartości makr.
MM
9
Myślę, że kiedy pisał, truemiał na myśli „wartość prawdziwego porównania” czy coś takiego, a nie makrotrue
MM
1
@KevinDong; Tak, projekt C99 ma podobny paragraf.
haccks
1
@haccks: możesz bez wahania powiedzieć o tym „identyczny”. Po prostu polubiłem twój cytat, ponieważ byłem zbyt leniwy, aby przeglądać po akapicie, ponieważ odnoszę się do c99, kiedy kiedykolwiek było to potrzebne. I udało mi się go znaleźć, po prostu przeszukując go przez ctrl+ f;)
dhein
2
@MooingDuck: NaN == NaNjest fałszywe i NaN != NaNprawdziwe. Nie ma problemu z tym stwierdzeniem.
kennytm
51

Nie jest to wyraźnie wskazane w C11. Wszystkie operacje na poziomie języka zwrócą wartość 1 jako prawdziwą (i zaakceptują wszystkie wartości niezerowe, w tym NaN, jako prawdziwe).

  • Jeśli _Boolmartwisz się, to prawda musi wynosić 1, ponieważ standard wymaga, aby zawierała tylko 0 i 1. (§6.2.5 / 2).
  • Również w <stdbool.h>makro truerozszerza się do 1(§7.18 / 3)
  • ==, !=, <, >, <=I >=powrót 0 lub 1 (w §6.5.8 / 6, §6.5.9 / 3).
  • !, &&I ||powrót 0 lub 1 (§6.5.3.3 / 5 §6.5.13 / 3, §6.5.14 / 3)
  • defined rozwija się do 0 lub 1 (§6.10.1 / 1)

Ale wszystkie standardowe funkcje biblioteczne, np. Po islowerprostu mówią „niezerowe” dla prawdziwości (np. §7.4.1 / 1, §7.17.5.1 / 3, §7.30.2.1 / 1, §7.30.2.2.1 / 4).


§6.2.5 / 2 : Obiekt zadeklarowany jako typ _Booljest wystarczająco duży, aby przechowywać wartości 0 i 1.

§6.5.5.3 / 5 : Wynik logicznego operatora negacji !wynosi 0, jeśli wartość jego operandu jest nierówna w porównaniu z 0, 1, jeśli wartość jego argumentu jest równa 0.…

§6.5.8 / 6 : Każdy z operatorów <(mniejszy niż), >(większy niż), <=(mniejszy lub równy) i >=(większy lub równy) daje 1, jeśli określona relacja jest prawdziwa i 0, jeśli to fałsz. 107)…

§6.5.9 / 3 : Operatory ==(równe) i !=(nie równe) są analogiczne do operatorów relacyjnych, z wyjątkiem ich niższego priorytetu. 108) Każdy z operatorów daje 1, jeśli określona relacja jest prawdą i 0, jeśli jest fałszywy. …

§6.5.13 / 3 : &&Operator powinien dać 1, jeśli oba jego argumenty są nierówne do 0; …

§6.5.14 / 3 : ||Operator powinien dać 1, jeśli którykolwiek z jego argumentów jest nierówny 0; …

§6.10.1 / 1 :… może zawierać jednoargumentowe wyrażenia operatora w postaci - defined identifier- lub - defined ( identifier )-, które dają 1, jeśli…

§7.4.1 (Funkcje klasyfikacji znaków) / 1 : Funkcje w tej podrozdziale zwracają wartość różną od zera (prawda) wtedy i tylko wtedy, gdy…

§7.18 / 3 : Pozostałe trzy makra są odpowiednie do użycia w #ifdyrektywach przetwarzania wstępnego. Są to - true- które rozwija się do stałej liczby całkowitej 1,…

§7.17.5.1 / 3 : Funkcja atomic_is_lock_freeogólna zwraca wartość różną od zera (prawda) wtedy i tylko wtedy, gdy operacje obiektu są wolne od blokad. …

§7.30.2.1 (Funkcje klasyfikacji szerokich znaków) / 1 : Funkcje w tej podrozdziale zwracają wartość różną od zera (prawda) wtedy i tylko wtedy, gdy…

§7.30.2.2.1 / 4 : iswctypeFunkcja zwraca wartość różną od zera (prawda) wtedy i tylko wtedy, gdy…

kennytm
źródło
23

Istnieją dwa obszary standardu, o których należy pamiętać, mając do czynienia z wartościami boolowskimi (przez które mam na myśli wartości prawda / fałsz, a nie określony bool/_Booltyp C ) w C.

Pierwszy dotyczy wyniku wyrażeń i można go znaleźć w różnych częściach C11 6.5 Expressions(na przykład operatory relacyjne i operatory równości). Najważniejsze jest to, że za każdym razem, gdy wartość logiczna jest generowana przez wyrażenie, to ...

... daje 1, jeśli określona relacja jest prawdą i 0, jeśli jest fałszywa. Wynik ma typ int.

Zatem tak, wynikiem każdego wyrażenia generującego wartości logiczne będzie jeden dla prawdy lub zero dla fałszu. Dopasowuje Co będzie można znaleźć w stdbool.hktórych poziom makr truei falsesą zdefiniowane w ten sam sposób.

Należy jednak pamiętać, że zgodnie z zasadą solidności „bądź konserwatywny w tym, co wysyłasz, a liberalny w tym, co akceptujesz”, interpretacja liczb całkowitych w kontekście boolowskim jest nieco bardziej swobodna.

Ponownie, z różnych części 6.5zobaczysz język taki jak:

||Operator wyda 1, jeśli jeden z jej parametrami porównanie różnych od 0; w przeciwnym razie zwraca 0. Wynik ma typ int.

Z tego (i innych części) wynika, że ​​zero jest uważane za fałszywe, a każda inna wartość jest prawdziwa.


Nawiasem mówiąc, język określający, jaka wartość jest używana do generowania i interpretacji wartości logicznych, pojawia się również w C99 i C89, więc są one dostępne już od dłuższego czasu. Nawet K&R (drugie wydanie ANSI-C i pierwsze wydanie) określiło to w przypadku segmentów tekstu, takich jak:

Wyrażenia relacyjne, takie jak i > ji wyrażenia logiczne połączone przez &&i ||są zdefiniowane tak, aby miały wartość, 1jeśli są prawdziwe, a 0jeśli fałszywe.

W części testowej if, while, for, itp, „prawda” oznacza po prostu „non-zero”.

&&Operator ... zwraca 1, jeśli oba jego operandy porównać nierówne zeru, 0 w przeciwnym wypadku.

||Operator zwraca wartość 1, jeśli ... albo jej argumenty porównania różnych od zera, i 0 w przeciwnym razie.

Makra w programie stdbool.hpojawiają się również w C99, ale nie w C89 ani K&R, ponieważ ten plik nagłówkowy nie istniał w tym momencie.

paxdiablo
źródło
2
pamiętać, że ||, ==, !=itp wydajność int, a nie typu boolean
MM
2
Głosuję na to pytanie na poprawne. Dla mnie pytanie dotyczy również operatorów relacyjnych, a nie makr.
ckruczek
„W części testowej if, while, for, itp,«prawda»oznacza po prostu«non-zero».” To najistotniejsza część odpowiedzi i moim zdaniem jest to niefortunny wybór Dennisa Ritchiego z dawnych czasów. Każdy, kto napisał funkcje, które zwracają kody błędów jako wartość zwracaną, zwykle ma, #define noErr 0a każdy niezerowy kod błędu jest błędem. A potem problemem jest prostota i piękno if ( ready_to_do_something() ){do_something();} nie działa. Musi brzmieć: if ( !not_ready_to_do_something() ){do_something();}„Jest wiele kłamstw, ale tylko jedna prawda”. PRAWDA powinno być 0.
robert bristow-johnson
Z ciekawości, w jaki sposób pierwsze szkice reguł C określały zachowanie „&&” i „||” w przypadku, gdy operandy miały wartości inne niż 0 lub 1? Cytowany tekst mówi „wyrażenia logiczne” połączone przez && i ||, ale co, jeśli te operatory łączą rzeczy inne niż wyrażenia logiczne?
supercat
1
@sdenham, tak. W najwcześniejszym egzemplarzu K&R, jaki posiadam (pierwsze wydanie, nakład 14, jeden tak wczesny, że wspomina o cechach sprzętowych czterech typowych maszyn, PDP-11, Honeywell-6000, IBM-370 i Interdata-8/32), A.7.6/7/10/11(relacyjne / równość / logiczne i / logiczne lub) wszystkie określają, że daje wynik 0 lub 1. Zaktualizuj odpowiedź, aby to uwzględnić.
paxdiablo
10

Mylisz wiele różnych rzeczy: instrukcje sterujące, operatory i typy boolowskie. Każdy ma swoje własne zasady.

Instrukcje kontrolne działają jak na przykład ifinstrukcja C11 6.4.8.1:

W obu formach pierwsze podstacja jest wykonywana, jeśli wyrażenie porównuje nierówność z 0.

while, forEtc mają taką samą regułę. Nie ma to nic wspólnego z „prawdą” lub „fałszem”.

Jeśli chodzi o operatory, które rzekomo dają wynik boolowski, w rzeczywistości dają one wynik into wartości 1 lub 0. Na przykład operatory równości, C11 6.5.9:

Każdy z operatorów daje 1, jeśli określona relacja jest prawdą, i 0, jeśli jest fałszywa

Wszystko to wynika z tego, że C nie miał typu boolowskiego aż do roku 1999, a nawet kiedy go otrzymał, powyższe zasady nie zostały zmienione. Tak więc w przeciwieństwie do większości innych języków programowania, w których instrukcje i operatory dają typ boolowski (jak C ++ i Java), po prostu dają intznak o wartości zero lub niezerowej. Na przykład sizeof(1==1)da 4 w C, ale 1 w C ++.

Rzeczywisty typ boolowski w C jest nazwany _Booli wymaga nowoczesnego kompilatora. Nagłówek stdbool.hmakra Definiuje bool, trueoraz false, że rozszerzają się _Bool, 1a 0odpowiednio (dla kompatybilności z C ++).


Uważa się jednak, że dobrą praktyką programowania jest traktowanie instrukcji sterujących i operatorów tak, jakby faktycznie wymagały / dawały typ boolowski. Niektóre standardy kodowania, takie jak MISRA-C, zalecają taką praktykę. To jest:

if(ptr == NULL)zamiast if(ptr).

if((data & mask) != 0)zamiast if(data & mask).

Celem takiego stylu jest zwiększenie bezpieczeństwa typów za pomocą narzędzi do analizy statycznej, co z kolei zmniejsza liczbę błędów. Prawdopodobnie ten styl ma znaczenie tylko wtedy, gdy używasz analizatorów statycznych. Chociaż w niektórych przypadkach prowadzi to na przykład do bardziej czytelnego, samodokumentującego się kodu

if(c == '\0') 

Dobrze, zamiar jest jasny, kod samodokumentuje się.

przeciw

if(c) 

Zły. Może oznaczać wszystko i musimy poszukać typu, caby zrozumieć kod. Czy jest to liczba całkowita, wskaźnik czy znak?

Lundin
źródło
1
sizeof(bool)jest specyficzna dla implementacji w C ++. Zobacz stackoverflow.com/questions/4897844/is-sizeofbool-defined .
David Hammen
@DavidHammen Podobnie jak sizeof (0 == 0) jest również zdefiniowane w ramach implementacji. To tylko przykład.
Lundin
Myślałem, że C zmienił reguły dla typów boolowskich. Inne rodzaje typów uintN (w tym typy "bitowe" wielu starszych kompilatorów) przechowują niższe N ​​bitów wartości i ignorują wszystkie wyższe bity, podczas gdy nowe typy logiczne efektywnie "lub" łączą wszystkie bity.
supercat
1
Powinno tak być if(ptr != NULL), czy może if(!ptr)?
Mathieu K.
1
if(c == '\0')nadaje się do szczególnie powszechnego błędu programowania przez początkujących if(c = '\0'), więc go unikam. Zgoda, if(c)jest źle ... powinno być na przykładif(valveIsOpen)
aja
4

Programowałem w wielu językach. Widziałem, że prawda wynosi 1 lub -1 w zależności od języka. Logika stojąca za prawdziwym byciem 1 polegała na tym, że bit miał wartość 0 lub 1. Logika prawdziwego bycia -1 była taka, że! operator był dopełnieniem. Zmienił wszystkie jedynki na 0 i wszystkie 0 na 1 w int. Tak więc dla int,! 0 = -1 i! (- 1) = 0. To mnie zaskoczyło na tyle, że nie porównuję czegoś, aby było == prawda, ale zamiast tego porównuję to, aby było! = Fałsz. W ten sposób mój styl programowania działa w każdym języku. Więc moja odpowiedź brzmi: nie martw się o to, ale programuj tak, aby Twój kod działał poprawnie.

Russell Hankins
źródło
jak można ! zamień wszystkie 0 na 1 i nadal produkuje 0 dla! 5?
codehot
@codeshot Nie można. Ale chodzi o to, że nie wszystkie języki traktują operand! jako wartość logiczna. Trochę uczty! jako C ~ - to znaczy bitowe uzupełnienie. W takim przypadku określenie wynikowej wartości wymaga przede wszystkim znajomości typu zmiennej, więc! (Uint32_t) 5 będzie równe 4 294 967 290. Ale! 0 to nadal 4 294 967 295, a 4 294 967 295 to prawda.
Pegasus Epsilon
1

Tej odpowiedzi należy przyjrzeć się bliżej.

Rzeczywista definicja w C ++ jest taka, że ​​wszystko inne niż 0 jest traktowane jako prawdziwe. Dlaczego jest to istotne? Ponieważ C ++ nie wie, czym jest liczba całkowita, na podstawie tego, jak o niej myślimy - tworzymy to znaczenie, wszystko, co zawiera, to powłoka i reguły dotyczące tego, co to znaczy. Wie jednak, czym są bity, które składają się na liczbę całkowitą.

1 jako liczba całkowita jest luźno reprezentowana w bitach, powiedzmy 8-bitowa wartość int ze znakiem jako 0000 0001. Wiele razy to, co widzimy wizualnie, jest trochę kłamstwem, -1 jest znacznie bardziej powszechnym sposobem przedstawienia tego ze względu na charakter ze znakiem „integer”. Naprawdę nie mogę mieć na myśli prawdziwej, właściwej, dlaczego? Ponieważ NIE jest to operacja 1111 1110. To naprawdę poważny problem dla boolean. Kiedy mówimy o wartości logicznej, jest to tylko 1 bit - to naprawdę proste, 0 to fałsz, a 1 to prawda. Wszystkie operacje logiczne są banalne. Dlatego „-1” powinno być oznaczone jako „prawda” dla liczb całkowitych (ze znakiem). 1111 1111 NOT'ed staje się 0000 0000 - logika się utrzymuje i jesteśmy w porządku. Niepodpisane liczby int są nieco skomplikowane i w przeszłości były znacznie częściej używane - gdzie 1 oznacza prawdę, ponieważ łatwo jest zasugerować logikę, że ”

To jest wyjaśnienie. Mówię, że przyjęta odpowiedź jest zła - w definicji C / C ++ nie ma jasnej definicji. Wartość logiczna jest wartością logiczną, możesz traktować liczbę całkowitą jako wartość logiczną, ale fakt, że wynik jest liczbą całkowitą, nie mówi nic o faktycznie wykonywanej operacji.

Tommy
źródło
4
Pytanie dotyczyło C, a nie C ++.
glglgl
0

Stało się tak z powodu operatorów relacyjnych w twoim printfoświadczeniu.

Operator ==i operator!=

Ponieważ (0 == 0)tak jest, daje wartość1

podczas gdy (0 != 0)nie jest prawdą, daje wartość 0.

SKD
źródło