Странная булевская переменная C++ кастинг поведения (верный! =true)

Просто читайте на внутреннем университетском потоке:

#include <iostream>
using namespace std;

union zt
{
 bool b;
 int i;
};

int main()
{
 zt w;
 bool a,b;
 a=1;
 b=2;
 cerr<<(bool)2<<static_cast<bool>(2)<<endl;                      //11
  cerr<<a<<b<<(a==b)<<endl;                                      //111
 w.i=2;
 int q=w.b;
 cerr<<(bool)q<<q<<w.b<<((bool)((int)w.b))<<w.i<<(w.b==a)<<endl; //122220
 cerr<<((w.b==a)?'T':'F')<<endl;                                 //F
}

Так a,b и w.b все объявляются как bool. a присвоен 1, b присвоен 2, и внутреннее представление w.b изменяется на 2 (использующий a union).

Таким образом, весь из a,b и w.b будет true, но a и w.b не будет равно, таким образом, это могло бы означать, что вселенная повреждается (true!=true)

Я знаю, что эта проблема является более теоретической, чем практичный (программист пользы не хочет изменять внутреннее представление a bool), но вот вопросы:

  1. Это хорошо? (это было протестировано с g ++ 4.3.3), я имею в виду, компилятор должен знать, что во время булева сравнения какое-либо ненулевое значение могло бы означать верный?
  2. Вы знаете какой-либо случай, где этот угловой случай мог бы стать реальной проблемой? (Например, при загрузке двоичных данных из потока)

Править:

Три вещи:

  1. bool и int имейте различные размеры, это хорошо. Но что, если я использую char вместо int. Или когда sizeof(bool)==sizeof(int)?

  2. Дайте ответ на эти два вопроса, которые я задал, если это возможно. Я на самом деле интересуюсь ответами на вторые вопросы также, потому что по моему честному мнению, по встроенным системам (который мог бы быть системами на 8 битов) это могло бы быть настоящей проблемой (или не).

  3. Новый вопрос: это действительно неопределенное поведение? Если да, почему? В противном случае, почему? Нет ли никакие предположения на булевых операторах сравнения в спецификациях?

10
задан timrau 15 August 2012 в 00:05
поделиться

8 ответов

  1. Это нормально? (это было протестировано с g ++ 4.3.3) Я имею в виду, должен ли компилятор знать, что во время логического сравнения любое ненулевое значение может означать истину?

Любое целочисленное значение, которое не равно нулю (или указатель, который не равен NULL), представляет правда. Но при сравнении целых чисел и bool перед сравнением bool преобразуется в int.

  1. Знаете ли вы какой-нибудь случай, когда этот угловой случай может стать реальной проблемой? (Например, при бинарной загрузке данных из потока)

Это всегда реальная проблема.

  1. Это нормально?

    Я не знаю, указано ли что-нибудь в спецификациях по этому поводу. Компилятор всегда может создать такой код: ((a! = 0) && (b! = 0)) || ((a == 0) && (b == 0)) при сравнении двух логических значений, хотя это может снизить производительность.

    На мой взгляд, это не ошибка, а неопределенное поведение. Хотя я думаю, что каждый разработчик должен рассказывать пользователям, как булевы сравнения выполняются в их реализации.

Если мы обратимся к вашему последнему образцу кода, то и a, и b имеют логические значения и установлены в значение true, уважительно присваивая 1 и 2 (Noe the 1 и 2 исчезают они теперь просто правда).

Итак, разбиваем ваше выражение:

a!=0      // true (a converted to 1 because of auto-type conversion)
b!=0      // true (b converted to 1 because of auto-type conversion)

((a!=0) && (b!=0)) => (true && true)  // true ( no conversion done)

a==0      // false (a converted to 1 because of auto-type conversion)
b==0      // false (b converted to 1 because of auto-type conversion)

((a==0) && (b==0)) => (false && false) // false ( no conversion done)

((a!=0) && (b!=0)) || ((a==0) && (b==0)) => (true || false) => true

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

Но я не уверен, как это применимо к вашему исходному вопросу. При присвоении целого числа bool целое число преобразуется в bool (как описано несколько раз). Фактическое представление true не определено стандартом и может быть любым битовым шаблоном, который подходит для bool (вы не можете предполагать какой-либо конкретный битовый шаблон).

При сравнении bool с int сначала bool преобразуется в int затем сравнил.

  1. Любой реальный случай

    Единственное, что приходит мне в голову, - если кто-то читает двоичные данные из файла в структуру, которая имеет члены типа bool. Проблема может возникнуть, Есть проблемы с размером каждого объекта.
    Есть проблемы с представлением:

    • Целые числа (имеют порядок байтов)
    • Float (Представление не определено ((обычно зависит от используемого оборудования))
    • Bool (Двоичное представление не определено стандартом)
    • Struct (Padding между членами могут различаться)

    Для всего этого вам необходимо знать базовое оборудование и компилятор.Различные компиляторы или разные версии компилятора или даже компилятор с разными флагами оптимизации могут иметь разное поведение для всего вышеперечисленного.

    Проблема с Union

    struct X
    {
        int  a;
        bool b;
    };
    

    , поскольку люди упоминают, что написание до «a», а затем чтение от «b» не определено.
    Почему: потому что мы не знаем, как «a» или «b» представлены на этом оборудовании. Запись в «a» заполнит биты в «a», но как это отразится на битах в «b». Если ваша система использовала 1 байт bool и 4 байта int с младшим байтом в младшей памяти наивысшим байтом в верхней памяти, то запись 1 в 'a' поместит 1 в 'b'. Но как тогда ваша реализация представляет собой логическое значение? Истина представлена ​​1 или 255? Что произойдет, если вы поставите 1 в 'b' и для всех других случаев использования true будет использовано 255?

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

    Таким образом, эти варианты использования не определены. но не запрещены стандартом. Причина, по которой они разрешены, заключается в том, что вы, возможно, провели исследование и обнаружили, что в вашей системе с этим конкретным компилятором вы можете провести некоторую бесплатную оптимизацию, сделав эти предположения. Но имейте в виду, что любые изменения в предположениях нарушат ваш код.

    Также при сравнении двух типов компилятор будет выполнять некоторые автопреобразования перед сравнением, помните, что два типа перед сравнением преобразуются в один и тот же тип. Для сравнения между целыми числами и bool число bool преобразуется в целое число, а затем сравнивается с другим целым числом (преобразование преобразует false в 0 и true в 1). Если оба преобразуемых объекта имеют тип bool, то преобразование не требуется и сравнение выполняется с использованием логической логики.

    Но имейте в виду, что любые изменения в предположениях нарушат ваш код.

    Также при сравнении двух типов компилятор будет выполнять некоторые автопреобразования перед сравнением, помните, что два типа перед сравнением преобразуются в один и тот же тип. Для сравнения между целыми числами и bool число bool преобразуется в целое число, а затем сравнивается с другим целым числом (преобразование преобразует false в 0 и true в 1). Если оба преобразуемых объекта имеют тип bool, то преобразование не требуется и сравнение выполняется с использованием логической логики.

    Но имейте в виду, что любые изменения в предположениях нарушат ваш код.

    Также при сравнении двух типов компилятор будет выполнять некоторые автопреобразования перед сравнением, помните, что два типа перед сравнением преобразуются в один и тот же тип. Для сравнения между целыми числами и bool число bool преобразуется в целое число, а затем сравнивается с другим целым числом (преобразование преобразует false в 0 и true в 1). Если оба преобразуемых объекта имеют тип bool, то преобразование не требуется и сравнение выполняется с использованием логической логики.

    Для сравнения между целыми числами и bool логическое значение преобразуется в целое число, а затем сравнивается с другим целым числом (преобразование преобразует false в 0 и true в 1). Если оба преобразуемых объекта имеют тип bool, то преобразование не требуется и сравнение выполняется с использованием логической логики.

    Для сравнения между целыми числами и bool число bool преобразуется в целое число, а затем сравнивается с другим целым числом (преобразование преобразует false в 0 и true в 1). Если оба преобразуемых объекта имеют тип bool, преобразование не требуется, и сравнение выполняется с использованием логической логики.

9
ответ дан 3 December 2019 в 14:06
поделиться

Если вы читаете член объединения, который является другим членом, чем последний член, который был записан, вы получите неопределенное поведение. Запись члена типа int с последующим чтением члена объединения bool может привести к тому, что в любой последующей точке программы произойдёт что угодно.

Единственное исключение - когда объединения представляют собой объединение структур, и все структуры содержат общую начальную последовательность в в этом случае можно прочитать общую последовательность.

17
ответ дан 3 December 2019 в 14:06
поделиться

Обычно при присвоении произвольного значения bool компилятор преобразует его для вас:

int x = 5;
bool z = x; // automatic conversion here

Эквивалентный код, сгенерированный компилятором, будет больше похож на:

bool z = (x != 0) ? true : false;

Однако компилятор выполнит это преобразование только один раз. Было бы неразумно предполагать, что любой ненулевой битовый шаблон в переменной bool эквивалентен true , особенно для выполнения логических операций, таких как и . Полученный ассемблерный код будет громоздким.

Достаточно сказать, что если вы используете структуры данных union , вы знаете, что делаете, и можете запутать компилятор.

9
ответ дан 3 December 2019 в 14:06
поделиться

Логическое значение - один байт, а целое число - четыре байта. Когда вы присваиваете 2 целому числу, четвертый байт имеет значение 2, но первый байт имеет значение 0. Если вы прочитаете логическое значение вне объединения, оно захватит первый байт.

Изменить: Ооо. Как отмечает Олег Жилин, это относится только к процессорам с прямым порядком байтов. Спасибо за исправление.

2
ответ дан 3 December 2019 в 14:06
поделиться

Я считаю, что то, что вы делаете, называется каламбуром: http://en.wikipedia.org/wiki/Type_punning

1
ответ дан 3 December 2019 в 14:06
поделиться

Хм, странно, я получаю другой вывод с кодовой панели:

11
111
122222
T

Код мне тоже кажется правильным, может это ошибка компилятора?
См. Здесь

0
ответ дан 3 December 2019 в 14:06
поделиться

Просто чтобы записать мою точку зрения:

  1. Это нормально?

    Я не знаю, указывается ли что-нибудь в спецификации по этому поводу. Компилятор всегда может создать такой код: ((a! = 0) && (b! = 0)) || ((a == 0) && (b == 0)) при сравнении двух логических значений, хотя это может снизить производительность.

    На мой взгляд, это не ошибка, а неопределенное поведение. Хотя я думаю, что каждый разработчик должен рассказывать пользователям, как булевы сравнения выполняются в их реализации.

  2. Любой реальный случай

    Единственное, что приходит мне в голову, если кто-то читает двоичные данные из файла в структуру , у которых есть члены типа bool. Проблема может возникнуть, если файл был создан другой программой, которая написала 2 вместо 1 вместо bool (возможно, потому, что он был написан на другом языке программирования).

    Но это может означать плохую практику программирования.

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

0
ответ дан 3 December 2019 в 14:06
поделиться

Отвечая на поставленные вопросы, я считаю, что поведение в порядке и не должно быть проблемой в реальном мире. Поскольку у нас нет ^^ в C ++, я бы предложил! Bool ==! Bool в качестве безопасного метода сравнения bool.

Таким образом, каждое ненулевое значение в переменной bool будет преобразовано в ноль, а каждый ноль преобразуется в какое-то ненулевое значение, но, скорее всего, одно и то же для любой операции отрицания.

-1
ответ дан 3 December 2019 в 14:06
поделиться
Другие вопросы по тегам:

Похожие вопросы: