Как стать автором
Обновить

Комментарии 54

Может быть, потому что switch() работает с целыми числами, а true и false заменяются typedef-ами на 1 и 0 при компиляции? В неинициализированной переменной может быть любой мусор. Условие if(!var) сработает только если значение 0 или false, а вот switch() уже нет, там нужно точное совпадение.
Условие if(!var) сработает только если значение 0 или false, а вот switch() уже нет, там нужно точное совпадение.

Умный компилятор (например, GCC) пользуется знанием того, что bool-переменная может иметь значение либо true, либо false, и (в любой корректной программе) третьего не дано.
Может вы правы, у меня GCC не ругается и не предупреждает если записать
bool var = 10;
Возможно он сразу приводит ее к «true» дополнительной инструкцией. Но если в коде нет явной инициализации, то нет и приведения, и в ячейке хранится любое число. Неявное приведение к int в switch() видимо не настолько «умное» чтобы его преобразовать к true/false, а скорее всего лишние проверки не добавляются по соображениям производительности: в конце-концов, Undefined behaviour лежит на совести программиста, а не компилятора. Он может толкьо предупредить.
Возможно он сразу приводит ее к «true» дополнительной инструкцией
«true» понятие относительное, смотря как использовать.
Если if (var) то будет true и будет выполняться (if делает сравнение на 0)
Если if (var == true) то будет false, ибо true обозначен как «1», а у нас в переменной 10
Нет, неправда. Проверьте сами и убедитесь.
Это если совсем старый C. Начиная с C99 bool это нативный тип и компилятор умеет к нему приводить, поэтому вашем примере if(var == true) проверка пройдет.
Точно, проверил. Преобразует к единице на этапе инициализации. То есть bool var = 10 сделает var равной единице.

Вот ведь как в память врезалось…
В какой-то старой вижуал студии помню так и работало: если bool b = 10, то потом в ней хранилось именно это значение.
GCC для примера с if-ами вообще странный код генерирует. Во-первых, он выкидывает напрочь ветку default из switch и выводит false, потому что в первом case-е сравнивает булевскую переменную с 1, когда не совпадает (а там же мусор, так что не совпадает) — сразу переходит на cout<<«false\n»;. А в обоих ифах gcc тупо сравнивает булевскую переменную с 0 и довольный выводит true. Ассемблерный код для if(t[0]) и if(t[0]==true) абсолютно одинаковый:
0x80486e8 <+0x0048> 80 3e 00 cmpb $0x0,(%esi)
0x80486eb <+0x004b> 75 43 jne 0x8048730 <main()+144>
Подозреваю, что MVC для switch-а честно сравнивает переменную сначала с 1, потом с 0 и в результате попадает в default.
А вот при явном присвоении «bool var=10;» gcc еще при компиляции преобразует 10 в true (то есть в 1), что в отладке видно.

В gcc с включенной оптимизацией компилируете?

Таки да, забыл выключить. Теперь для switch-а результат «superposition» и bool сравнивается как int, с 1 и 0, для if-ов по прежнему true. Да и ассемблерный код для них все равно одинаковый. В общем-то логично, что gcc if(t[0]) и if(t[0]==true) одинаково воспринимает, так же как и if(!t[0]) и if(t[0]==false).
НЛО прилетело и опубликовало эту надпись здесь
Вряд ли. По крайней мере, у меня собранный под GCC код
  bool b = 2;
  switch (b)
  {
    case true:
    {
      cout << "T" << endl;
      break;
    }
    case false:
    {
      cout << "F" << endl;
      break;
    }
    default:
    {
      cout << "D" << endl;
    }
  }

выводит «T», а не «D».
Замените bool на int и подставьте в условие вместо true "== 0" и вместо false "== 1" и всё станет на свои места.

Сам по себе bool не существует, это, если не ошибаюсь, int, а сравнение используется только с нулём или единицей. Если его не инициализировать, то там может быть любая отличная от них ересь.
bool по стандарту char
bool, по стандарту, это bool.
Он имел ввиду размер переменной типа `bool`. То бишь в размер машинного слова и на дефолтных архитектурах может принимать значения от 0 до 255.
А где в стандарте указано, что sizeof(char) == sizeof(bool)?
Это не стандартом диктуется же, а архитектурой. Исполнение команд размером меньше машинного слова вроде как вообще не предусмотрено ибо неэффективно. По крайней мере на современных архитектурах. Если не ошибась даже вроде есть механизмы обработки сразу нескольких булевых значений хранящихся в одном слове.
Однако, слово может быть 32/64 бита (внутри). А bool в пакованных структурах компилятор вроде как приводит к char. В не пакованных хз, да и надо ли оно вникать…
НЛО прилетело и опубликовало эту надпись здесь
подставьте в условие вместо true "== 0" и вместо false "== 1"

Разве не наоборот? Вместо true "== 1" и вместо false "== 0".

Наоборот естественно… спешил.

С UB бывают всякие «весёлые» эффекты. Например, null reference может быть одновременно == 0 и != 0, может быть равна другой такой же ссылке или не равна, в зависимости от версии компилятора и флагов оптимизации: пришлось рефакторить всю систему обработки ошибок. Простое правило: если вы полагаетесь на UB, вы добавляете в код немного святого рандома. Каждый компилятор вправе превратить этот код во что угодно, и он превратит. Garbage in => garbage out.

Хм, странно, что на Линуксе нет. Именно на Линуксе на GCC ловил похожий баг: неинициализированная переменная типа bool, при приведении к int получалось не 0/1, а то значение байта, что лежало в bool. На Windows отрабатывало правильно.
ответ на вопрос почему — в стандарте
standard
Values of type bool are either true or false(48)
[Note:There are no signed,unsigned,short, or long bool types or values.— end note]
Values of type bool participate in integral promotions (4.5)

48) Using a bool value in ways described by this International Standard as “undefined,” such as by examining the value of an
uninitialized automatic object, might cause it to behave as if it is neither true nor false.

Хоть один комментарий по делу.

Вообще-то, нет ничего хорошего в том, что bool может иметь третье значение, не true и не false. Это противоречит основному пониманию логических переменных, общепринятому в мире (спросите у любого не знающего С++, но знакомого с логическими операциями, И, ИЛИ, сколько значений может иметь логическая переменная), а язык заявляет реализацию ООП, который исходит из объектов и свойств реальных объектов мира. И во всех книжках по С++, что я читал, (и уверен в абсолютном большинстве книг ро С++), тип bool представлен как имеющий два возможных значения.
Это приводит к тому, что для нормальной работы на языке мало прочесть книгу по С++, мало к тому прочесть еще и Саттера, Мейерса, но надо еще и стандарт знать. Причем, это все совершенно неочевидно после прочтения книг, которые пишутся как раз для того, чтобы можно было после них нормально писать код.
Ну а то, что над этим «превосходством» над обычной логикой, где у логической переменной два значения, и планирование логических операций строится в рамках этого диапазона, будут смеяться пишущие на других языках — это лишь мелкий побочный эффект.
А много ли людей пишут код, делая switch для булевой переменной вместо одного if?
if ( a ) 
{
}
else
{
}
Правильно. Вот в этом и есть корень зла. Не надо усложнять там, где усложнять, по факту, нечего.
Всё верно, bool-переменная может иметь только два значения: true, false. Никакого третьего значения там быть не может. Undefined behavior, по определению, означает, что программа может вести себя как угодно, и никакую логику к ней применить уже нельзя.
В SQL бывает третье значение — NULL и зачастую это бывает полезно.
А по моему это нормально. С++ остается высокопроизводительным языком, и нагружать его лишними проверками не стоит. Если нужны все проверки и защита от дурака, то есть более высокоуровневые языки. А тут остаются предупреждения компилятора, и если уж программист использует switch() для булевой переменной и игнорирует предупреждения — сам виноват.
нет ничего хорошего в том, что bool может иметь третье значение, не true и не false.

Это только в случае неопределённого поведения (undefined behavior). Но в случае неопределённого поведения, по стандарту, может происходить вообще что угодно, на усмотрение компилятора. Потому оно и неопределённое. Так что здесь нечему удивляться.
Это приводит к тому, что для нормальной работы на языке мало прочесть книгу по С++, мало к тому прочесть еще и Саттера, Мейерса, но надо еще и стандарт знать. Причем, это все совершенно неочевидно после прочтения книг, которые пишутся как раз для того, чтобы можно было после них нормально писать код.

В любой нормальной книжке по C++ должна обязательно быть написана одна простая мысль: «Избегайте неопределённого поведения». Если в вашей книжке этого не сказано, то я даже не знаю. Наверное стоит взять другую книгу.
В отладочной версии, свежевыделенный кусок кучи заливается неким магическим значением (0xCD), в целях отладки. Это фича Visual C++, причем отладочной конфигурации.
www.microsoft.com/msj/1198/c/c1198.aspx

Выделенная в стеке память в отладочной конфигурации заливается 0xCC.
Да, но удивление вызывает не это, а то, что MSVC генерирует код для случая "bool-значение не равно ни true, ни false" — случая, запрещённого стандартом.
Для сравнения, умный компилятор вообще удаляет из этого кода ветку default, потому что знает, что стандарт гарантирует, что эта ветка не понадобится.
НЛО прилетело и опубликовало эту надпись здесь
Под clang так же; а под MSVC — сравнивает с единицей.
(Причём и clang, и GCC удаляют обе недостижимые ветви прямо на этапе компиляции.)
НЛО прилетело и опубликовало эту надпись здесь
Стандарт гарантирует, что проверки младшего бита достаточно. (Как и сравнения с единицей достаточно.)
Выбор из многих возможных способов отличить ноль от единицы — личное дело разработчиков компилятора. Наверное, на каких-то процессорах эффективнее одно, на каких-то — другое.
Стандарт гарантирует

Сильное заявление. А пруфы можно?

Чуть выше в комментариях уже процитировали: «Values of type bool are either true or false»

Пожалуй так, но только не потому что они "true or false", а потому что
"Values of type bool participate in integral promotions (4.5)":
4.5.6 A prvalue of type bool can be converted to a prvalue of type int, with false becoming zero and true becoming one.
Вы же о побитовых операциях говорите и сравнении с целым.

НЛО прилетело и опубликовало эту надпись здесь
В исходниках chrome можно найти файлик, который отвечает именно за «излечение» таких вот неправильных bool-ов. Заодно где-то я видел объяснение того, почему такое поведение получается.

Беда именно в случае, когда в bool переменной оказывается значение, которое не 0 и не 1. При этом присвоить в bool такое значение нельзя — будет явно присвоено значение 0 или 1.

Хуже другое. Утверждается, что следующий код может выдать одновременно «true» и «false»:

bool x;
if (x)
    printf("true\n");
if (!x)
    printf("false\n");


Причем если бы x был int, такого бы не произошло. Причина в том, что для int всегда будет сравнение с нулем, а для bool может быть сравнение с единицей.

В линуксе (вернее в GCC) судя по всему вызов new POD или new POD[size] вызывает заодно зануление данных (а скорее всего используется calloc() для аллокации памяти).
Вот более корректный тест для вашей суперпозиции:
http://coliru.stacked-crooked.com/a/713a15859944fd94

Возможно, Вы будете приятно удивлены, прилинковав к проекту какую-нибудь библиотеку.
(Linux, GCC)
Заголовок спойлера
using namespace std;
int main(int argc, char** argv) 
{
    bool *t = new bool[1];
    switch (t[0])
    {
    case true:
        cout << "true\n";
        break;
    case false:
        cout << "false\n";
        break;
    default:
        cout << "superposition\n";
        break;
    }
    PQclear(NULL);
    return 0;
}

Перед return стоит вызов функции из библиотеки libpq. Теперь Программа выводит «false»
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории