В C ++ битовое представление (и даже размер) bool
определяется реализацией;как правило, он реализован в виде типа char
, принимающего в качестве возможных значений 1 или 0.
Если вы установите для него значение, отличное от допустимого (в данном конкретном случае путем наложения псевдонима bool
черезchar
и изменяя его битовое представление), вы нарушаете правила языка, поэтому может произойти все что угодно.В частности, в стандарте прямо указано, что «сломанный» bool
может вести себя как true
и false
(или ни true
, ни false
) одновременно:
Использование значения bool
способами, описанными в этом международном стандарте как «неопределенные», например, при проверке значения неинициализированного автоматического объекта, может привести к тому, что он будет вести себя так, как если бы он не был ни true
, ни false
(C ++ 11, [basic.fundamental], примечание 47)
В данном конкретном случае вы можете увидеть, как это закончилось в этомстранная ситуация : первое if
компилируется в
movzx eax, BYTE PTR [rbp-33]
test al, al
je .L22
, который загружает T
в eax
(с нулевым расширением) и пропускает печать, если все это ноль;следующее, если вместо этого
movzx eax, BYTE PTR [rbp-33]
xor eax, 1
test al, al
je .L23
Тест if(T == false)
преобразуется в if(T^1)
, который переворачивает только младший бит.Это было бы нормально для действительного bool
, но для вашего "сломанного" это не режет.
Обратите внимание, что эта странная последовательность генерируется только при низких уровнях оптимизации;на более высоких уровнях это обычно сводится к проверке ноль / ненулевое значение, и последовательность, подобная вашей, вероятно, станет одной тестовой / условной ветвью .В любом случае вы получите странное поведение в других контекстах, например, при суммировании значений bool
с другими целыми числами:
int foo(bool b, int i) {
return i + b;
}
становится
foo(bool, int):
movzx edi, dil
lea eax, [rdi+rsi]
ret
где dil
"trust" равен 0/1.
Если ваша программа полностью на C ++, тогда решение простое: не разбивайте значения bool
таким образом, избегайте путаницы с их битовым представлением ивсе будет хорошо;в частности, даже если вы присваиваете целое число bool
, компилятор выдаст необходимый код, чтобы убедиться, что полученное значение является действительным bool
, поэтому ваш bool T = 3
действительно безопасен, а T
будетв конечном итоге true
в его кишках.
Если вместо этого вам нужно взаимодействовать с кодом, написанным на других языках, который может не совпадать с представлением о bool
, просто избегайте bool
для«граничный» код, и маршалировать его как целое число соответствующего размера.Это будет работать в условных условиях.так же хорошо.
Обновление о Fortran / совместимости проблемы
Отказ от ответственности все, что я знаю о Fortran, это то, что я читаю сегодня утром на стандартных документах,и что у меня есть несколько перфокарт с листингами на Фортране, которые я использую в качестве закладок, так что будьте осторожны со мной.
Прежде всего, этот тип взаимодействия языков не является частью языковых стандартов,но о платформе ABI.Поскольку мы говорим о Linux x86-64, соответствующий документ System V x86-64 ABI .
Прежде всего, нигде не указано, что тип C _Bool
(которыйопределяется как C ++ bool
в примечании 3.1.2 †) имеет какую-либо совместимость с Fortran LOGICAL
;в частности, в 9.2.2 таблица 9.2 указывает, что «обычный» LOGICAL
отображается на signed int
.О TYPE*N
типах говорится, что
Обозначение «TYPE*N
» указывает, что переменные или агрегированные члены типа TYPE
должны занимать N
байт памяти.
(там же)
Для LOGICAL*1
явно не указан эквивалентный тип, и это понятно: он даже не стандартный;действительно, если вы попытаетесь скомпилировать программу на Фортране, содержащую LOGICAL*1
в режиме совместимости с Фортраном 95, вы получите предупреждения об этом как по ifort
./example.f90(2): warning #6916: Fortran 95 does not allow this length specification. [1]
logical*1, intent(in) :: x
------------^
, так и по gfort
./example.f90:2:13:
logical*1, intent(in) :: x
1
Error: GNU Extension: Nonstandard type declaration LOGICAL*1 at (1)
, поэтомуводы уже запутаны;поэтому, комбинируя два правила, приведенных выше, я бы пошел на signed char
, чтобы быть в безопасности.8 * Однако : ABI также указывает:
Значения для типа LOGICAL
.TRUE.
реализованы как 1, а .FALSE.
реализованы как 0.
Итак, если у вас есть программа, которая хранит что-либо, кроме 1 и 0, в значении LOGICAL
, , то вы уже не в спецификации на стороне Фортрана !Вы говорите:
Фортран logical*1
имеет то же представление, что и bool
, но в фортране, если биты 00000011, это true
, в C ++ оно не определено.
Последнее утверждение неверно, стандарт Фортрана не зависит от представлений, а ABI прямо говорит об обратном.Действительно, вы можете легко увидеть это в действии, проверив вывод gfort для LOGICAL
сравнения :
integer function logical_compare(x, y)
logical, intent(in) :: x
logical, intent(in) :: y
if (x .eqv. y) then
logical_compare = 12
else
logical_compare = 24
end if
end function logical_compare
станет
logical_compare_:
mov eax, DWORD PTR [rsi]
mov edx, 24
cmp DWORD PTR [rdi], eax
mov eax, 12
cmovne eax, edx
ret
Вы заметите, чтомежду двумя значениями есть прямая cmp
, без предварительной нормализации их (в отличие от ifort
, что более консервативно в этом отношении).
Еще интереснее: независимо от того, что говорит ABI, если по умолчанию используется ifortиспользует нестандартное представление для LOGICAL
;это объясняется в документации по коммутаторам -fpscomp logicals
, в которой также указаны некоторые интересные подробности о LOGICAL
и совместимости между языками:
Указывает, что целые числа с ненулевым значениемзначение обрабатывается как true, целые числа с нулевым значением рассматриваются как false.Буквальная константа. TRUE.имеет целочисленное значение 1 и литеральную константу .FALSE.имеет целочисленное значение 0. Это представление используется в выпусках Intel Fortran до версии 8.0 и Fortran PowerStation.
По умолчанию используется значение fpscomp nologicals
, которое указывает, что нечетные целочисленные значения (младший бит один) обрабатываются какИстинные и четные целые значения (младший бит ноль) считаются ложными.
Литеральная константа .TRUE.имеет целочисленное значение -1 и литеральную константу .FALSE.имеет целочисленное значение 0. Это представление используется Compaq Visual Fortran.Внутреннее представление значений LOGICAL не указано стандартом Fortran. Программы, которые используют целочисленные значения в контекстах LOGICAL или передают значения LOGICAL процедурам, написанным на других языках, являются непереносимыми и могут работать некорректно.Intel рекомендует избегать практики кодирования, которая зависит от внутреннего представления значений LOGICAL.
(выделение добавлено)
Теперь внутреннее представление LOGICAL
обычно не должно быть проблемой, поскольку, насколько я понимаю, если вы играете «по правилам» и не пересекаете языковые границы, вы не заметите этого.Для стандартной совместимой программы нет «прямого преобразования» между INTEGER
и LOGICAL
;Единственный способ, которым я вижу, что вы можете засунуть INTEGER
в LOGICAL
, похоже, TRANSFER
, который по сути не переносим и не дает никаких реальных гарантий, или нестандартный INTEGER
<-> LOGICAL
преобразование по заданию.
Последнее задокументировано gfort, чтобы всегда приводить к ненулевым значениям -> .TRUE.
, нулю -> .FALSE.
и , которые вы видите что во всех случаях генерируется код, чтобы это произошло (даже если это сложный код в случае ifort с унаследованным представлением), поэтому вы не можете подсунуть произвольное целое число в LOGICAL
таким образом.
logical*1 function integer_to_logical(x)
integer, intent(in) :: x
integer_to_logical = x
return
end function integer_to_logical
integer_to_logical_:
mov eax, DWORD PTR [rdi]
test eax, eax
setne al
ret
Обратное преобразование для LOGICAL*1
является прямым целочисленным нулевым расширением (gfort), поэтому, чтобы соблюдать договор в документации, связанной выше, он явно ожидает, что значение LOGICAL
будет0 или 1.
Но в целом ситуация для этих преобразований составляет немного из беспорядок , поэтому я бы просто держался от них подальше.
Итак, короче говоря: избегайте ставить INTEGER
daТо есть в значения LOGICAL
, так как это плохо даже в Fortran, и убедитесь, что вы используете правильный флаг компилятора, чтобы получить ABI-совместимое представление для логических значений, и совместимость с C / C ++ должна быть в порядке.Но для большей безопасности я бы просто использовал обычный char
на стороне C ++.
Наконец, из того, что я извлекаю из документации , в ifort есть некоторая встроенная поддержка взаимодействия с C, включая логические значения;Вы можете попытаться использовать его.