Есть способ получить переменную bool не равную ни true, ни false. При этом разные компиляторы работают по-разному. В Microsoft 2005 в следующем фрагменте кода можно сравнивать переменную b c true или false, и мы зайдем в else (если мне не изменяет память). В gcc (точно в последних версиях) уже необходимо сравнивать с функцией bool g(), чтобы зайти в else.
Есть два вопроса:
1. Какое поведение должно быть по стандарту, может ли переменная типа bool быть не равна ни true, ни false?
2. Какое поведение в других компиляторах/версиях?
#include <stdio.h>
int f()
{
return 4;
}
bool g()
{
return true;
}
int main() {
//bool b = reinterpret_cast<bool (*)()>(f)();
bool b = ((bool (*)())f)();
if (b == g()) // b == true
{
printf("true");
}
else if (b == !g()) // b == false
{
printf("false");
}
else
{
printf("bool is not equal to true or false");
}
return 0;
}
P.S. Аналогичная ситуация с языком C и типом _Bool, не равным ни 1, ни 0.
Здравствуйте, Jukier, Вы писали:
J>Есть два вопроса:
J>1. Какое поведение должно быть по стандарту,
По стандарту тут UB:
A function pointer can be explicitly converted to a function pointer of a different type. The effect of calling
a function through a pointer to a function type (8.3.5) that is not the same as the type used in the definition
of the function is undefined.
Я понимаю, что сама задача определить как компилятор представляет тип bool на низком уровне может быть занятной. Но ты делаешь это неправильно, а твой код ломается раньше.
J> может ли переменная типа bool быть не равна ни true, ни false?
В правильной программе на С++ — не может.
J>2. Какое поведение в других компиляторах/версиях?
Всё то же UB.
Здравствуйте, Jukier, Вы писали:
J>может ли переменная типа bool быть не равна ни true, ни false?
Конечно, ведь ты же сам "сказал" компилятору (reinterpret_cast) что то что записано в
int это не
int вовсе а
bool, он же согласно букве
закона стандарта тебе не перечит — верит на слово.
J>Какое поведение должно быть по стандарту
В общем случае UB.
На деле если тот тип что на месте
int'а "влазит" в тот тип что на месте
bool'а — может и обойдется
Здравствуйте, watchmaker, Вы писали:
W>Здравствуйте, Jukier, Вы писали:
J>>Есть два вопроса:
J>>1. Какое поведение должно быть по стандарту,
W>По стандарту тут UB: A function pointer can be explicitly converted to a function pointer of a different type. The effect of calling
W>a function through a pointer to a function type (8.3.5) that is not the same as the type used in the definition
W>of the function is undefined.
Спасибо за указание места, где это определено.
W>Я понимаю, что сама задача определить как компилятор представляет тип bool на низком уровне может быть занятной. Но ты делаешь это неправильно, а твой код ломается раньше.
В Microsoft VS 2005 bool занимает 1 байт: true == 1, false == 0. А в этом примере b == 4 (и там, как я помню, для захода в else функция g() не была нужна). Меня удивило, что в gcc (g++) есть разница между сравнением с true и false напрямую и через функцию.
J>>2. Какое поведение в других компиляторах/версиях?
W>Всё то же UB.
Если есть под рукой последняя Microsoft Visual Studio (или более поздняя, чем 2005), то буду благодарен, если посмотрите, как работает там приведенный кусок кода с явным сравнением с true и false.
Здравствуйте, _niko_, Вы писали:
__>Здравствуйте, Jukier, Вы писали:
J>>может ли переменная типа bool быть не равна ни true, ни false?
__>Конечно, ведь ты же сам "сказал" компилятору (reinterpret_cast) что то что записано в int это не int вовсе а bool, он же согласно букве закона стандарта тебе не перечит — верит на слово.
Этот момент я понял, но в стандарте есть про приведение других типов к bool, где сказано, что все ненулевое — это true.
J>>Какое поведение должно быть по стандарту
__>В общем случае UB.
__>На деле если тот тип что на месте int'а "влазит" в тот тип что на месте bool'а — может и обойдется
Спасибо за ответ, но если "влазит", то тоже не обойдется, проверено (см. ответ на пред. пост).
Здравствуйте, Jukier, Вы писали:
J> Меня удивило, что в gcc (g++) есть разница между сравнением с true и false напрямую и через функцию.
Я боюсь, что у тебя уже появляются не совсем верные ассоциации между представлением bool и «сравнением напрямую и через функцию». То есть связь там действительно может быть (об этом ниже), но формально в твоём примере до этого не доходит. У тебя там UB при вызове функций (а не при сравнении bool) — и на этом можно заканчивать разговор, код может делать что угодно и это уже не обязательно связано с самим bool. Вот, например, один из компиляторов решает что будет выводить ещё до запуска программы:
http://goo.gl/Hr9xWL — то есть там сравнений вообще не делается, а вся программа просто вырождается в единственный вызов печати. Чем это вырождение объясняется? Твой экспериментальный код не даёт ответа.
J>В Microsoft VS 2005 bool занимает 1 байт: true == 1, false == 0. А в этом примере b == 4 (и там, как я помню, для захода в else функция g() не была нужна).
Короче, тут всё не так просто. То что в памяти bool занимает один байт (и имеет два разрешенных значения 0 и 1) совсем не означает, что в регистре он будет представлен таким же образом, и тем более это не означает, что при передачи в другие процедуры (написанные в том числе на других языках) будет сохраняться то же соглашение. В общем случае не верно, что во всех трёх вышеописанных ситуациях bool на низком уровне хранится и трактуется одинаковым образом.
И чтобы в этом всём не запутаться придумали
документацию — ABI. И лучше документацию читать
Вот, например, популярная платформа
system V amd64 abi. Если открыть этот документ, то можно прочитать, что в памяти bool всегда занимает 1 байт и всегда равен 0 (ложь) или 1 (истина). Но тот же bool в регистре ведёт себя по другому — истиной считается уже не только 1, но и вообще любые ненулевые значения. При вызове же процедур значение bool хранится в младшем бите, при этом остальные биты младшего байта нулевые, а остальные биты — не определены.
Смог бы ты без документации своими экспериментами эти правила вывести? Вряд-ли. Да и бессмысленное это занятие. Прочитать — и проще, и надёжнее.
Начинать нужно с учебников и справочников, а не бросаться сразу писать глючный код и пытаться его объяснить.
Впрочем, после прочтения документации можно уже и проверять полученные знания
# nasm -f elf64 -o fn.o fn.asm
global foo, bar, baz
foo:
mov rax, 18364758544493064704
ret
bar:
mov rax, 18437098717332255489
ret
baz:
mov rax, 3
ret
Так согласно ABI функция foo возвращает значение false, функция bar — значение true, а функция baz — запрещённое значение.
То есть
любой компилятор C/C++ на упомянутой платформе будет корректно (и полностью определённо) работать с результатом вызова foo или bar, но поведение при вызове baz неопределено.
Небольшая проверка:
#include <cstdio>
extern "C" bool foo();
extern "C" bool bar();
extern "C" bool baz();
void test(const char* name, bool (*fn)()) {
printf("%s:\n", name);
const char* str[2] = {"\tfalse", "\ttrue"};
switch(fn()) {
case false: puts("\tfalse"); break;
case true: puts("\ttrue"); break;
default: puts("\t?"); break;
}
puts(str[fn()]);
}
int main() {
#define TEST(fn) test(#fn, fn)
TEST(foo);
TEST(bar);
TEST(baz);
}
И вот такие возможные выводы наблюдаются при разных компиляторах и разных их настройках:
| | gcc 4.8.2 -O1 |
| | foo:
false
false
bar:
true
true
baz:
?
|
| | |
| | gcc 4.8.2 -O2 |
| | foo:
false
false
bar:
true
true
baz:
false
1�H��1�I��^H��H���PTI���@
|
| | |
| | clang 3.4-1 -O2 |
| | foo:
false
false
bar:
true
true
baz:
true
Segmentation fault (core dumped)
|
| | |
Видно, что с baz вечно происходит что-то странное. А вот foo и bar вполне работают однозначно (и будут работать так всегда на этой платформе с любым компилятором).
Здравствуйте, watchmaker, Вы писали:
W>Короче, тут всё не так просто...
Благодарен за столь полный ответ, с огромным удовольствием прочитал (все бы так отвечали).
W>Я боюсь, что у тебя уже появляются не совсем верные ассоциации между представлением bool и «сравнением напрямую и через функцию». То есть связь там действительно может быть (об этом ниже), но формально в твоём примере до этого не доходит. У тебя там UB при вызове функций (а не при сравнении bool) — и на этом можно заканчивать разговор, код может делать что угодно и это уже не обязательно связано с самим bool.
У меня вопросы возникают: как в каком компиляторе сделано и почему. То что UB, а дальше можно форматировать диск, меня не удовлетворяет

. Ну дальше сам почитаю, ещё раз спасибо.