Чи вказує стандарт С явно значення істини як 0 або 1?


86

Ми знаємо, що будь-які числа, які не дорівнюють 0, розглядаються як trueу C, тому ми можемо написати:

int a = 16;

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

Однак мені було цікаво, чи визначено true / false як 1/ 0в C, тому я спробував наведений нижче код:

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

Чи вказує стандарт C явно значення істинності true та false як 1і 0відповідно?


3
Думаю, це SO питання є актуальним
Імран Алі

3
Я намагаюся запустити це під gccз, -std=c89і це дає той самий результат.
Кевін Донг,

1
@Blackhole, з 15 до 0.
Артуро Торрес Санчес,

6
Майже дурний, але за десять років до SO / SE: c-faq.com/bool/bool2.html .
dave_thompson_085

1
Те, що false - це нуль, - це канон, однак істина, як правило, розглядається як "не нуль". Проте програмісти - це те, що вони є, ми всі використовували 1 як "не нуль" з різних причин. Вам рекомендується не довіряти істині рівно 1. Хоча (0 == 0) у вашому прикладі відповідає одному, щось на зразок (12 == 12) може так само легко мати значення 12; також "правда".
інженер

Відповіді:


96

Чи чітко вказує стандарт C значення істинності trueта falseяк 0і 1відповідно?

Стандарт C визначає trueі falseяк макрокоманди, в stdbool.hяких розширюється до 1і 0відповідно.

C11-§7.18:

Решта три макроси придатні для використання в #ifдирективах попередньої обробки. Вони є

true

яка розширюється до цілої константи 1,

false

яка розширюється до цілочислової константи 0[...]

Для операторів ==і !=, як говорить стандарт

C11-§6.5.9 / 3:

Оператори ==(дорівнює) та !=(не дорівнюють) є аналогами реляційних операторів, за винятком їх нижчого пріоритету. 108) Кожен з операторів дає, 1якщо вказане відношення є істинним і 0якщо воно хибним. Результат має тип int. Для будь-якої пари операндів відповідає істині саме одне із співвідношень.


20
Мені здається , що мова йде про 0 == 0і 0 != 0т.д., а НЕ значенні макросів.
MM

9
Я думаю, що коли він писав, trueвін мав на увазі "цінність справжнього порівняння" чи щось інше, а не макросtrue
М.М.

1
@KevinDong; Так, проект С99 має подібний абзац.
хаки

1
@haccks: ви можете сказати про це, не соромлячись, "однаково". Я просто розібрав вашу цитату, оскільки мені було лінь шукати по абзацу, оскільки я посилаюся на c99, коли це колись потрібно. І мені вдалося його знайти, просто ctrlf
здійснивши

2
@MooingDuck: NaN == NaNневірно і NaN != NaNправда. З цим твердженням немає проблем.
kennytm

51

Це прямо не зазначено в C11. Усі операції на рівні мови повернуть значення 1 як істинний (і приймати будь-які ненульові значення, включаючи NaN, як істинні).

  • Якщо вас турбує питання _Bool, тоді true має бути 1, оскільки стандарт вимагає, щоб він містив 0 і 1. (§6.2.5 / 2).
  • Також у <stdbool.h>макросі trueрозгортається до 1(§7.18 / 3)
  • ==, !=, <, >, <=І >=повертають 0 або 1 (§6.5.8 / 6, §6.5.9 / 3).
  • !, &&І ||повертати 0 або 1 (§6.5.3.3 / 5, §6.5.13 / 3, §6.5.14 / 3)
  • defined розширюється до 0 або 1 (§6.10.1 / 1)

Але всі стандартні функції бібліотеки, наприклад, islowerпросто кажуть "ненульові" для правдивого (наприклад, §7.4.1 / 1, §7.17.5.1 / 3, §7.30.2.1 / 1, §7.30.2.2.1 / 4).


§6.2.5 / 2 : Об’єкт, оголошений як тип, _Boolє достатньо великим для зберігання значень 0 та 1.

§6.5.5.3 / 5 : Результат логічного оператора заперечення !дорівнює 0, якщо значення його операнда порівнюється не рівно 0, 1, якщо значення його операнда порівнюється рівним 0.…

§6.5.8 / 6 : Кожен з операторів <(менше), >(більше), <=(менше або дорівнює) та >=(більше або дорівнює) повинен дати 1, якщо вказане відношення є істинним, і 0, якщо воно є хибним. 107) ...

§ 6.5.9 / 3 : Оператори ==(дорівнює) та !=(не дорівнюють) є аналогами реляційних операторів, за винятком їх нижчого пріоритету.108) Кожен з операторів дає 1, якщо вказане відношення є істинним, і 0, якщо воно є помилковий. ...

§ 6.5.13 / 3 : &&Оператор 1, якщо обидва його операнди порівнюються нерівними з 0; ...

§ 6.5.14 / 3 : ||Оператор 1, якщо будь-який з його операндів порівнюється нерівним 0; ...

§6.10.1 / 1 :… він може містити одинарні операторні вирази форми -defined identifier - або - defined ( identifier )- які мають значення 1, якщо…

§7.4.1 (Функції класифікації символів) / 1 : Функції цього підпункту повертають ненульове значення (істинне значення) тоді і лише тоді, коли ...

§7.18 / 3 : Решта три макроси придатні для використання в#if директивах попередньої обробки. Вони - true- що розширюється до цілої константи 1, ...

§ 7.17.5.1 / 3 :atomic_is_lock_free загальна функція повертає ненульове (істинне) тоді і тільки тоді, коли операції з об’єктом не мають блокування. ...

§7.30.2.1 (Широкі функції класифікації символів) / 1 : Функції цього підпункту повертають ненульові (істинні) тоді і лише тоді, коли ...

§7.30.2.2.1 / 4 : iswctypeфункція повертає ненульове (істинне) тоді і лише тоді, коли ...


23

Є дві області стандарту, з якими вам слід пам’ятати, маючи справу з булевими значеннями (маючи на увазі істинні / хибні значення, а не конкретний bool/_Boolтип С ) у мові C.

Перший пов’язаний з результатом виразів і може бути знайдений у різних частинах C11 6.5 Expressions(наприклад, реляційні оператори та оператори рівності). Суть полягає в тому, що всякий раз, коли булеве значення генерується виразом, воно ...

... дає 1, якщо вказане відношення є істинним, і 0, якщо воно хибне. Результат має тип int.

Отже, так, результат будь-якого булево-генеруючого виразу буде одиницею для true, або нулем для false. Це відповідає тому, що ви знайдете stdbool.hтам, де стандартні макроси trueі falseвизначаються однаково.

Однак майте на увазі, що, дотримуючись принципу стійкості "будь консервативним у тому, що ти надсилаєш, ліберальним у тому, що ти приймаєш", інтерпретація цілих чисел у логічному контексті є дещо більш спокійною.

Знову ж таки, з різних частин 6.5ви побачите такі мови:

||Оператор повинен дати 1 , якщо будь-який з операндів порівнювати НЕ дорівнює 0; в іншому випадку це дає 0. Результат має тип int.

З цього (та інших частин) очевидно, що нуль вважається помилковим, а будь-яке інше значення - істинним.


Окрім того, мова, яка вказує, яке значення використовується для булевої генерації та інтерпретації, також з'являється ще в C99 та C89, тому вони існують вже досить давно. Навіть K&R (друге видання ANSI-C та перше видання) зазначили, що з такими сегментами тексту, як:

Реляційні вирази типу i > jі логічні вирази, пов’язані між собою &&та ||визначаються таким чином, що мають значення, 1якщо істинно, а 0якщо хибно.

У тестовій частині if, while, forі т.д., «правда» просто означає «Не одно нулю».

&&Оператор ... повертає 1 , якщо обидва операнда порівняння дорівнює нулю, 0 в іншому випадку.

||Оператор ... повертає 1 , якщо або його операнди порівняння дорівнює нулю, і 0 в іншому випадку.

Макроси в також stdbool.hз'являються в C99, але не в C89 або K&R, оскільки цього заголовного файлу на той момент не існувало.


2
зауважте, що ||, ==і !=т. д. дають intне булевий тип
ММ

2
Я голосую за це питання за правильне. Для мене питання також стосується реляційних операторів, а не макросів.
ckruczek

«У тестовій частині if, while, forі т.д.," Правда "просто означає« не нуль ".» Це основна частина відповіді і, на мій погляд, невдалий вибір Денніса Річі з давніх-давен. Той, хто написав функції, які повертають коди помилок як значення, що повертаються, як правило, #define noErr 0і будь-який ненульовий код помилки є помилкою. І тоді проблема полягає в простоті та красі if ( ready_to_do_something() ){do_something();} не працює. Це повинно бути if ( !not_ready_to_do_something() ){do_something();}"Існує багато фальшів, але правда одна". TRUE має бути 0.
Роберт Брістоу-Джонсон,

З цікавості, як перші проекти правил C визначали поведінку "&&" та "||" у випадку, коли операнди мали значення, відмінні від 0 або 1? У тексті, який ви цитуєте, сказано "логічні вирази", з'єднані && та ||, але що, якщо ці оператори з'єднують інші речі, крім логічних виразів?
суперкіт

1
@sdenham, так. У найранішій копії K&R, що є у мене (перше видання, тираж 14, один настільки ранній, що згадує апаратні характеристики чотирьох типових машин, PDP-11, Honeywell-6000, IBM-370 та Interdata-8/32), A.7.6/7/10/11(реляційна / рівність / логічна та / логічна або) вказують, що в результаті вона дає 0 або 1 Оновили відповідь, щоб включити це.
paxdiablo

10

Ви змішуєте багато різних речей: оператори управління, оператори та логічні типи. У кожного свої правила.

ifОперативні оператори працюють, як, наприклад, твердження, C11 6.4.8.1:

В обох формах перший підзапис виконується, якщо вираз порівнює нерівне 0.

while, forтощо мають те саме правило. Це не має нічого спільного з "істинним" чи "хибним".

Що стосується операторів, які нібито дають булевий результат, вони насправді дають intзначення зі значенням 1 або 0. Наприклад, оператори рівності, C11 6.5.9:

Кожен з операторів видає 1, якщо вказане відношення є істинним, і 0, якщо воно хибне

Все вищесказане пояснюється тим, що C не мав логічного типу до 1999 року, і навіть коли він отримав такий, вищезазначені правила не були змінені. Отже, на відміну від більшості інших мов програмування, де оператори та оператори видають логічний тип (наприклад, C ++ та Java), вони просто дають значення intзі значенням нуль чи ні. Наприклад, sizeof(1==1)дасть 4 на C, але 1 на C ++.

Фактичний булевий тип на мові C називається _Boolі вимагає сучасного компілятора. Заголовок stdbool.hвизначає макроси bool, trueі false, які розширюються до _Bool, 1і 0відповідно (для сумісності з C ++).


Однак вважається хорошою практикою програмування поводитися з операторами управління та операторами так, ніби вони насправді вимагали / дали булевий тип. Деякі стандарти кодування, такі як MISRA-C, рекомендують таку практику. Це є:

if(ptr == NULL)замість if(ptr).

if((data & mask) != 0)замість if(data & mask).

Метою такого стилю є підвищення безпеки типу за допомогою інструментів статичного аналізу, що в свою чергу зменшує помилки. Можливо, цей стиль має значення лише у тому випадку, якщо ви використовуєте статичні аналізатори. Хоча в деяких випадках, наприклад, це призводить до більш читабельного коду, що самодокументується

if(c == '\0') 

Добре, намір зрозумілий, код самодокументується.

проти

if(c) 

Погано Це може означати що завгодно, і нам доведеться шукати тип, cщоб зрозуміти код. Це ціле число, вказівник чи символ?


1
sizeof(bool)є конкретною реалізацією в C ++. Див. Stackoverflow.com/questions/4897844/is-sizeofbool-defined .
Девід Хаммен,

@DavidHammen Так само, як sizeof (0 == 0) також визначається реалізацією. Це лише приклад.
Лундін,

Я думав, що C змінив правила для булевих типів. Інші типи типів uintN (включаючи багато типів "бітових" компіляторів) зберігають нижчі N бітів значення та ігнорують будь-які старші біти, тоді як нові булеві типи ефективно "або" об'єднують усі біти.
supercat

1
Це має бути if(ptr != NULL), чи можливо if(!ptr)?
Матьє К.

1
if(c == '\0')піддається особливо поширеній у початківців помилці кодування if(c = '\0'), тому я уникаю цього. Згоден, if(c)погано ... так повинно бути, наприклад,if(valveIsOpen)
aja

4

Я програмував на багатьох мовах. Я бачив істинні значення 1 або -1 залежно від мови. Логіка справжнього буття 1 полягала в тому, що біт був або 0, або 1. Логіка правдивого буття -1 полягала в тому, що! оператор був своїм доповненням. Він змінив усі 1 на 0 і всі 0 на 1 в int. Отже, для int,! 0 = -1 та! (- 1) = 0. Це достатньо мене спокусило, що я не порівнюю щось із == істиною, а замість цього порівнюю з! = False. Таким чином, мій стиль програмування працює на будь-якій мові. Тож моя відповідь полягає в тому, щоб не турбуватися про це, а запрограмувати так, щоб ваш код працював коректно в будь-якому випадку.


як можна ! змінити всі 0 на 1 і все одно виробляти 0 для! 5?
кодовий знімок

@codeshot Це не може. Але суть, яку він робить, полягає в тому, що не всі мови розглядають операнд! як логічне значення. Деякі частування! як C ~ - тобто побітове доповнення. У цьому випадку для визначення результуючого значення потрібно спочатку знати тип змінної, отже! (Uint32_t) 5 буде 4 294 967 290. Але! 0 - це все-таки 4 294 967 295, а 4 294 967 295 - істинно.
Пегас Епсілон,

1

Цю відповідь потрібно розглянути дещо уважніше.

Фактичне визначення в C ++ полягає в тому, що все, що не є 0, розглядається як істинне. Чому це актуально? Оскільки C ++ не знає, що таке ціле число, як ми про це думаємо - ми створюємо таке значення, все, що воно містить, це оболонка та правила того, що це означає. Він знає, що це за біти, які складають ціле число.

1, оскільки ціле число вільно представлене у бітах, скажімо, 8-бітний підписаний int як 0000 0001. Багато разів те, що ми бачимо візуально, є трохи брехнею, -1 є набагато більш поширеним способом представити його через підписаний характер з "цілого числа". 1 справді не може означати справжнє належне, чому? Оскільки це НЕ операція - це 1111 1110. Це справді головна проблема для логічної системи. Коли ми говоримо про булеве значення, це лише 1 біт - це насправді просто, 0 - неправда і 1 - істина. Всі логічні операції вважаються тривіальними. Ось чому «-1» слід позначати як «true» для цілих чисел (підписано). 1111 1111 NOT'ed стає 0000 0000 --- логіка справедлива, і ми добре. Непідписані ints трохи хитрі, і їх набагато частіше використовували в минулому - де 1 означає істину, оскільки легко зрозуміти логіку, що '

Це пояснення. Я вважаю, що прийнята відповідь тут неправильна - у визначенні C / C ++ немає чіткого визначення. Логічне значення є булевим значенням, ви можете розглядати ціле число як булеве значення, але той факт, що результат є цілим числом, нічого не говорить про побітну операцію, що фактично виконується.


4
Питання було про С, а не С ++.
glglgl

0

Це сталося через реляційних операторів у вашому printf заяві.

Оператор == та оператор!=

Оскільки (0 == 0)справедливо так, це дає значення1

тоді як, (0 != 0)не відповідає істині, тому дає значення 0.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.