Я видел следующий код в это сообщение 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 ++.

95
iammilind 19 Дек 2018 в 17:40

4 ответа

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

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

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

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

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

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

57
HostileFork says dont trust SE 19 Дек 2018 в 18:56

В вашем понимании битовых полей нет ничего плохого, что я вижу. Я вижу, что сначала вы переопределили 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
ar18 4 Янв 2019 в 01:47

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

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

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

10
Paul Ogilvie 19 Дек 2018 в 14:57

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

struct mystruct { int enabled:1; };

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

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

22
Community 19 Дек 2018 в 16:00