размер - функция bool c++




Установка дополнительных битов в bool делает его истинным и ложным одновременно (2)

В C ++ битовое представление (и даже размер) bool определяется реализацией; как правило, он реализован в виде типоразмера, принимающего 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;
}

becomes

foo(bool, int):
        movzx   edi, dil
        lea     eax, [rdi+rsi]
        ret

где dil является «доверенным», чтобы быть 0/1.

Если ваша программа полностью на C ++, то решение простое: не bool значения bool таким образом, избегайте путаницы с их битовым представлением, и все будет хорошо; в частности, даже если вы присваиваете целочисленное значение bool компилятор выдаст необходимый код, чтобы убедиться, что полученное значение является допустимым bool , поэтому ваш bool T = 3 действительно безопасен, и T итоге получит true в его кишках.

Если вместо этого вам нужно взаимодействовать с кодом, написанным на других языках, которые могут не иметь единого представления о том, что такое bool , просто избегайте bool для «граничного» кода и выставляйте его как целое число соответствующего размера. Это будет работать в условных условиях. так же хорошо.

Обновление о 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 явно не указан эквивалентный тип, и это понятно: это даже не стандарт; действительно, если вы попытаетесь скомпилировать программу на Fortran, содержащую LOGICAL*1 в режиме совместимости с Fortran 95, вы получите предупреждения об этом, оба от ifort

./example.f90(2): warning #6916: Fortran 95 does not allow this length specification.   [1]

    logical*1, intent(in) :: x

------------^

и по комфорту

./example.f90:2:13:
     logical*1, intent(in) :: x
             1
Error: GNU Extension: Nonstandard type declaration LOGICAL*1 at (1)

поэтому воды уже запутаны; так что, сочетая два правила выше, я бы пошел на signed char чтобы быть в безопасности.

Тем не менее : ABI также указывает:

Значения для типа LOGICAL : .TRUE. реализовано как 1 и .FALSE. реализовано как 0.

Итак, если у вас есть программа, которая хранит что-то кроме 1 и 0 в LOGICAL значении, вы уже не в спецификации на стороне Фортрана ! Ты говоришь:

Фортран logical*1 имеет то же представление, что и bool , но в фортране, если биты 00000011, это true , в С ++ он не определен.

Последнее утверждение неверно, стандарт Фортрана не зависит от представления, а 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

Вы заметите, что между ifort двумя значениями есть прямой cmp , без предварительной нормализации (в отличие от ifort , это более консервативно в этом отношении).

Еще интереснее: независимо от того, что говорит ABI, ifort по умолчанию использует нестандартное представление для LOGICAL ; это объясняется в документации по переключателю -fpscomp logicals , в которой также указаны некоторые интересные подробности о LOGICAL и кросс-языковой совместимости:

Указывает, что целые числа с ненулевым значением обрабатываются как true, целые числа с нулевым значением рассматриваются как false. Буквальная константа. TRUE. имеет целочисленное значение 1 и литеральную константу .FALSE. имеет целочисленное значение 0. Это представление используется в выпусках Intel Fortran до версии 8.0 и Fortran PowerStation.

По умолчанию используется fpscomp nologicals , который указывает, что нечетные целочисленные значения ( 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 в значения LOGICAL , поскольку это плохо даже в Fortran, и убедитесь, что вы используете правильный флаг компилятора для получения ABI-совместимого представления для логических значений, и совместимость с C / C ++ должна быть в порядке , Но для большей безопасности я бы просто использовал обычный char на стороне C ++.

Наконец, из того, что я извлекаю из документации , в ifort есть некоторая встроенная поддержка взаимодействия с C, включая логические значения; Вы можете попытаться использовать это.

Если я получу переменную bool и установлю ее второй бит на 1, тогда переменная будет иметь значение true и false одновременно. Скомпилируйте следующий код с помощью gcc6.3 с параметром -g ( gcc-v6.3.0/Linux/RHEL6.0-2016-x86_64/bin/g++ -g main.cpp -o mytest_d ) и запустите исполняемый файл. Вы получаете следующее.

Как T может быть равным true и false одновременно?

       value   bits 
       -----   ---- 
    T:   1     0001
after bit change
    T:   3     0011
T is true
T is false

Это может произойти, когда вы вызываете функцию на другом языке (скажем, Fortran), где определение true и false отличается от C ++. Для fortran, если какие-либо биты не равны 0, тогда значение равно true, если все биты равны нулю, тогда значение равно false

#include <iostream>
#include <bitset>

using namespace std;

void set_bits_to_1(void* val){
  char *x = static_cast<char *>(val);

  for (int i = 0; i<2; i++ ){
    *x |= (1UL << i);
  }
}

int main(int argc,char *argv[])
{

  bool T = 3;

  cout <<"       value   bits " <<endl;
  cout <<"       -----   ---- " <<endl;
  cout <<"    T:   "<< T <<"     "<< bitset<4>(T)<<endl;

  set_bits_to_1(&T);


  bitset<4> bit_T = bitset<4>(T);
  cout <<"after bit change"<<endl;
  cout <<"    T:   "<< T <<"     "<< bit_T<<endl;

  if (T ){
    cout <<"T is true" <<endl;
  }

  if ( T == false){
    cout <<"T is false" <<endl;
  }


}

/////////////////////////////////// // Функция Fortran, которая не совместима с C ++ при компиляции с ifort.

       logical*1 function return_true()
         implicit none

         return_true = 1;

       end function return_true

Это то, что происходит, когда вы нарушаете контракт с языком и компилятором.

Возможно, вы где-то слышали, что «ноль - это ложь» и «ненулевое - это истина». Это верно, когда вы придерживаетесь параметров языка, статически конвертируя int в bool или наоборот.

Он не работает, когда вы начинаете возиться с битными представлениями. В этом случае вы нарушаете свой контракт и вводите (по крайней мере) поведение, определяемое реализацией.

Просто не делай этого.

Это не ваше дело, как bool хранится в памяти. Это зависит от компилятора. Если вы хотите изменить значение bool , либо присвойте true / false , либо присвойте целое число и используйте надлежащие механизмы преобразования, предоставляемые C ++.

Стандарт C ++, используемый для фактического обращения к тому, как использование bool таким образом является порочным, плохим и злым ( «Использование значения bool способами, описанными в этом документе как« неопределенные », например, путем изучения значения неинициализированный автоматический объект может привести к тому, что он будет вести себя так, как если бы он не был ни true ни false . " ), хотя он был удален в C ++ 20 по редакционным причинам .





abi