비트 필드에 값을 할당해도 동일한 값이 반환되지 않는 이유는 무엇입니까? 1)

이 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 ++ 사양에 대한 답변을 참조하십시오 .



답변

비트 필드는 표준에 의해 엄청나게 잘못 정의되어 있습니다. 이 코드가 주어지면 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모든 형태의 비트 조작에 서명 된 유형을 사용하지 마십시오 .


답변

나는 이해할 수 없습니다. 어떻게 우리가 무언가를 설정했는데 그것이 그대로 나타나지 않는 것이 가능합니다.

왜 컴파일되고 오류가 발생하는지 묻고 있습니까?

예, 이상적으로는 오류가 발생합니다. 컴파일러의 경고를 사용하면 그렇습니다. 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-합법적으로 비트 필드가 “필요”할만큼 정교하다면 서비스를 제공 할만큼 충분히 정의되지 않은 것입니다.)


답변

이것은 구현 정의 동작입니다. 나는 당신이 이것을 실행하는 기계가 2s-compliment 부호있는 정수를 사용 int하고이 경우 if 문의 true 부분을 입력하지 않는 이유를 설명하기 위해 부호있는 정수로 취급한다고 가정하고 있습니다.

struct mystruct { int enabled:1; };

enable1 비트 비트 필드로 선언 합니다. 서명되었으므로 유효한 값은 -10입니다. 필드를 1오버플로하도록 설정하면 해당 비트가 다시 돌아갑니다 -1(정의되지 않은 동작).

부호 비트 필드를 처리 할 때 기본적으로 최대 값은 2^(bits - 1) - 1이다 0이 경우.


답변

2의 보수 시스템에서 가장 왼쪽 비트가 부호 비트라고 생각할 수 있습니다. 따라서 가장 왼쪽 비트가 설정된 부호있는 정수는 음수 값입니다.

1 비트 부호있는 정수가 있으면 부호 비트 만 있습니다. 따라서 1해당 단일 비트에 할당 하면 부호 비트 만 설정할 수 있습니다. 따라서 다시 읽을 때 값은 음수로 해석되므로 -1입니다.

1 비트 부호있는 정수가 보유 할 수있는 값은 다음 -2^(n-1)= -2^(1-1)= -2^0= -1과 같습니다.2^n-1= 2^1-1=0


답변

당으로 C ++ 표준 n4713 , 매우 유사한 코드가 제공됩니다. 사용되는 유형은 BOOL(사용자 정의)이지만 모든 유형에 적용 할 수 있습니다.

12.2.4

4bool 어떤 크기의 비트 필드(1 비트 비트 필드 포함)에true 또는 false 값이 저장되면 원래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 , 출력은 상기 표준 pasage의 지정과 같다 :

활성화 됨 (사용시 enum BOOL)


따라서 다른 답변이 거의없는 것처럼 그 유형은 단일 비트 비트 필드에 값 “1”을 저장할만큼 충분히 크지 않다는 결론을 내릴 수 있습니다 int.


답변

내가 볼 수있는 비트 필드에 대한 이해에는 잘못된 것이 없습니다. 내가 보는 것은 먼저 mystruct를 struct mystruct {int enabled : 1; } 그리고 as 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");
}


답변