Почему присвоение значения битовому полю не возвращает это же значение?

Я видел ниже код в этот пост Quora:

#include <stdio.h>

struct mystruct { int enabled:1; };
int main()
{
struct mystruct s;
s.enabled = 1;
if(s.enabled == 1)
printf("Is enabled\n"); // --> we think this to be printed
else
printf("Is disabled !!\n");
}

В обоих C & C ++, вывод кода неожиданный,

Выключен !!

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

Может кто-нибудь дать более подробное объяснение?

84

Решение

Битовые поля невероятно плохо определены стандартом. Учитывая этот код struct mystruct {int enabled:1;};, тогда мы не знать:

  • Сколько места это занимает — если есть биты / байты заполнения и где они расположены в памяти.
  • Где бит находится в памяти. Не определено и также зависит от порядка байтов.
  • Будь int:n битовое поле считается подписанным или неподписанным.

Что касается последней части, C17 6.7.2.1/10 говорит:

Битовое поле интерпретируется как имеющее целочисленный тип со знаком или без знака, состоящий из
указанное количество бит 125)

Ненормативная записка, поясняющая вышесказанное:

125) Как указано в 6.7.2 выше, если фактический используемый спецификатор типа int или typedef-имя, определенное как int,
затем определяется реализацией, является ли битовое поле подписанным или неподписанным.

В случае, если битовое поле следует рассматривать как signed int и вы делаете немного размера 1, тогда нет места для данных, только для знакового бита. По этой причине ваша программа может давать странные результаты на некоторых компиляторах.

Хорошая практика:

  • Никогда не используйте битовые поля для каких-либо целей.
  • Избегайте использования подписанных int введите для любой формы битовых манипуляций.
72

Другие решения

Я не могу понять, как это возможно, что мы что-то устанавливаем, и тогда это не появляется, как есть.

Вы спрашиваете, почему он компилирует и дает ошибку?

Да, в идеале это должно дать вам ошибку. И это так, если вы используете предупреждения вашего компилятора. В GCC, с -Werror -Wall -pedantic:

main.cpp: In function 'int main()':
main.cpp:7:15: error: overflow in conversion from 'int' to 'signed char:1'
changes value from '1' to '-1' [-Werror=overflow]
s.enabled = 1;
^

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

Чтобы добавить некоторый прескриптивизм, я повторю заявление @ Лундина: «Никогда не используйте битовые поля для каких-либо целей». Если у вас есть веские причины для получения низкоуровневых и точных сведений о разметке памяти, из-за которых вы могли бы подумать, что вам в первую очередь нужны битовые поля, другие связанные с вами требования почти наверняка столкнутся с их недостаточной спецификацией.

(TL; DR — если вы достаточно опытны, чтобы законно «нуждаться» в битовых полях, они недостаточно четко определены, чтобы обслуживать вас.)

52

Это поведение, определяемое реализацией. Я делаю предположение, что на машинах, на которых вы работаете, используются целые числа с двойным комплиментом и обрабатываются int в этом случае в виде целого числа со знаком, чтобы объяснить, почему вы не вводите, если true, часть инструкции if.

struct mystruct { int enabled:1; };

объявляет enable как 1-битное битовое поле. Поскольку он подписан, действительными значениями являются -1 а также 0, Установка поля в 1 переполняет этот бит, возвращаясь к -1 (это неопределенное поведение)

По сути, при работе со битовым полем со знаком максимальное значение 2^(bits - 1) - 1 который 0 в этом случае.

18

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

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

Значения, которые может содержать 1-битное целое число: -2^(n-1)= -2^(1-1)= -2^0= -1 а также 2^n-1= 2^1-1=0

7

В соответствии с Стандарт C ++ n4713, очень похожий фрагмент кода предоставляется. Используемый тип BOOL (обычай), но это может применяться к любому типу.

12.2.4

4 Если значение true или false хранится в битовом поле типа bool любого размера (включая одноразрядное битовое поле), оригинал bool Значение и значение битового поля должны сравниваться равными. Если значение перечислителя хранится в битовом поле с тем же типом перечисления, а количество битов в битовом поле достаточно велико для хранения
все значения этого типа перечисления (10.2), исходное значение перечислителя и значение битового поля
будет сравнивать равные
.
[ Пример:

enum BOOL { FALSE=0, TRUE=1 };
struct A {
BOOL b:1;
};
A a;
void f() {
a.b = TRUE;
if (a.b == TRUE)    // yields true
{ /* ... */ }
}

— конец примера]


На первый взгляд жирная часть открыта для интерпретации. Тем не менее, правильное намерение становится ясным, когда enum BOOL происходит от int,

enum BOOL : int { FALSE=0, TRUE=1 }; // ***this line
struct mystruct { BOOL enabled:1; };
int main()
{
struct mystruct s;
s.enabled = TRUE;
if(s.enabled == TRUE)
printf("Is enabled\n"); // --> we think this to be printed
else
printf("Is disabled !!\n");
}

С приведенным выше кодом это дает предупреждение без -Wall -pedantic:

предупреждение: «mystruct :: enabled» слишком мало, чтобы содержать все значения «enum BOOL»
struct mystruct { BOOL enabled:1; };

Выход:

Выключен !! (когда используешь enum BOOL : int)

Если enum BOOL : int сделано просто enum BOOL, тогда вывод будет таким, как указано выше в стандартной странице:

Включен (при использовании enum BOOL)


Следовательно, можно сделать вывод, также как и несколько других ответов, что int Тип недостаточно велик, чтобы хранить значение «1» в одном битовом поле.

4

В вашем понимании битовых полей нет ничего плохого, что я вижу. Что я вижу, так это то, что вы сначала переопределили mystruct как struct mystruct {int enabled: 1; } а потом как struct mystruct s;. То, что вы должны были закодировать, было:

#include <stdio.h>

struct mystruct { int enabled:1; };
int main()
{
mystruct s; <-- Get rid of "struct" type declaration
s.enabled = 1;
if(s.enabled == 1)
printf("Is enabled\n"); // --> we think this to be printed
else
printf("Is disabled !!\n");
}
0
По вопросам рекламы [email protected]