Здравствуйте, 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 вполне работают однозначно (и будут работать так всегда на этой платформе с любым компилятором).