Почему присвоение значения битовому полю не возвращает это же значение? - PullRequest
0 голосов
/ 19 декабря 2018

Я видел приведенный ниже код в этом сообщении 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 ++ вывод кода неожиданный ,

Отключено !!

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

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


Примечание : оба тега & требуются, потому что их стандарты немного отличаются для описания битовых полей.См. Ответы по C спецификации и C ++ спецификации .

Ответы [ 6 ]

0 голосов
/ 04 января 2019

В вашем понимании битовых полей нет ничего плохого, что я вижу.Я вижу, что вы сначала переопределили 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 голосов
/ 20 декабря 2018

Согласно стандарту 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"только в одном битовом поле.

0 голосов
/ 19 декабря 2018

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

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

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

0 голосов
/ 19 декабря 2018

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

struct mystruct { int enabled:1; };

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

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

0 голосов
/ 19 декабря 2018

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

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

Относительно последней части C17 6.7.2.1/10 говорит:

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

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

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

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

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

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

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

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

Да, в идеале она должна дать вам ошибку.И это так, если вы используете предупреждения вашего компилятора.В 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 - если вы достаточно искушены, чтобы законно «нуждаться» в битовых полях, они недостаточно четко определены, чтобы служить вам.)

...