Comments 54
"Undefined behavior" можете добавить в теги. Вообще странно, что его существование и разная обработка в зависимости от компилятора вас удивляет.
http://en.cppreference.com/w/cpp/language/ub
Условие if(!var) сработает только если значение 0 или false, а вот switch() уже нет, там нужно точное совпадение.
Умный компилятор (например, GCC) пользуется знанием того, что
bool
-переменная может иметь значение либо true
, либо false
, и (в любой корректной программе) третьего не дано.bool var = 10;
Возможно он сразу приводит ее к «true» дополнительной инструкцией. Но если в коде нет явной инициализации, то нет и приведения, и в ячейке хранится любое число. Неявное приведение к int в switch() видимо не настолько «умное» чтобы его преобразовать к true/false, а скорее всего лишние проверки не добавляются по соображениям производительности: в конце-концов, Undefined behaviour лежит на совести программиста, а не компилятора. Он может толкьо предупредить.
Возможно он сразу приводит ее к «true» дополнительной инструкцией«true» понятие относительное, смотря как использовать.
Если if (var) то будет true и будет выполняться (if делает сравнение на 0)
Если if (var == true) то будет false, ибо true обозначен как «1», а у нас в переменной 10
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 с включенной оптимизацией компилируете?
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"
Разве не наоборот? Вместо true "== 1" и вместо false "== 0".
С UB бывают всякие «весёлые» эффекты. Например, null reference может быть одновременно == 0 и != 0, может быть равна другой такой же ссылке или не равна, в зависимости от версии компилятора и флагов оптимизации: пришлось рефакторить всю систему обработки ошибок. Простое правило: если вы полагаетесь на UB, вы добавляете в код немного святого рандома. Каждый компилятор вправе превратить этот код во что угодно, и он превратит. Garbage in => garbage out.
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.
Хоть один комментарий по делу.
Это приводит к тому, что для нормальной работы на языке мало прочесть книгу по С++, мало к тому прочесть еще и Саттера, Мейерса, но надо еще и стандарт знать. Причем, это все совершенно неочевидно после прочтения книг, которые пишутся как раз для того, чтобы можно было после них нормально писать код.
Ну а то, что над этим «превосходством» над обычной логикой, где у логической переменной два значения, и планирование логических операций строится в рамках этого диапазона, будут смеяться пишущие на других языках — это лишь мелкий побочный эффект.
if ( a )
{
}
else
{
}
нет ничего хорошего в том, что bool может иметь третье значение, не true и не false.
Это только в случае неопределённого поведения (undefined behavior). Но в случае неопределённого поведения, по стандарту, может происходить вообще что угодно, на усмотрение компилятора. Потому оно и неопределённое. Так что здесь нечему удивляться.
Это приводит к тому, что для нормальной работы на языке мало прочесть книгу по С++, мало к тому прочесть еще и Саттера, Мейерса, но надо еще и стандарт знать. Причем, это все совершенно неочевидно после прочтения книг, которые пишутся как раз для того, чтобы можно было после них нормально писать код.
В любой нормальной книжке по C++ должна обязательно быть написана одна простая мысль: «Избегайте неопределённого поведения». Если в вашей книжке этого не сказано, то я даже не знаю. Наверное стоит взять другую книгу.
www.microsoft.com/msj/1198/c/c1198.aspx
Выделенная в стеке память в отладочной конфигурации заливается 0xCC.
bool
-значение не равно ни true
, ни false
" — случая, запрещённого стандартом.Для сравнения, умный компилятор вообще удаляет из этого кода ветку
default
, потому что знает, что стандарт гарантирует, что эта ветка не понадобится.(Причём и clang, и GCC удаляют обе недостижимые ветви прямо на этапе компиляции.)
Выбор из многих возможных способов отличить ноль от единицы — личное дело разработчиков компилятора. Наверное, на каких-то процессорах эффективнее одно, на каких-то — другое.
Стандарт гарантирует
Сильное заявление. А пруфы можно?
Пожалуй так, но только не потому что они "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.
Вы же о побитовых операциях говорите и сравнении с целым.
Второй вариант с if никогда не заходит в третий вариант.
А можно перевести на русский? А то получается примерно так:
Чтобы все наши устройства были в порядке,я в срочном порядке был призван к порядку, чтобы обрабатывать данные разных порядков на несколько порядков быстрее.
Беда именно в случае, когда в 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»
Когда переменная bool не true и не false одновременно