왜이 구조체 크기가 2가 아닌 3입니까?


91

이 구조체를 정의했습니다.

typedef struct
{
    char A:3;
    char B:3;
    char C:3;
    char D:3;
    char E:3;
} col; 

sizeof(col)나에게 (3)의 출력을 제공하지만 2이어야한다? 하나의 요소 만 언급하면 sizeof2가됩니다. 이유를 이해할 수 없습니다. 3 비트의 5 개 요소가 15 비트와 같고 2 바이트 미만입니다.

이와 같은 구조를 정의 할 때 "내부 크기"가 있습니까? 지금까지의 언어 개념에서 3이 아닌 2 바이트 크기를 예상했기 때문에 설명이 필요합니다.


4
아마도 정렬 최적화 일 것입니다. 다음 비트 크기가 실제 점유 공간에 맞지 않으면 새 바이트를 시작합니다.
πάντα ῥεῖ 2014

4
비트 패킹이 필요한 외부 제약 조건이 있고 플랫폼이 표준이 제공하는 것에 대한 추가 보증을 제공하지 않는 한, 비트 필드를 사용하는 데는 별 의미가 없습니다.
David Rodríguez-dribeas 2014

3
C의 경우 char를 사용하는 것이 int, stackoverflow.com/a/23987436/23118을 사용하는 것보다 이식성이 떨어 집니다.
hlovdal 2014

2
비트 필드에 대한 거의 모든 것은 구현 정의입니다. 다른 컴파일러에서 다른 답변을 얻을 수 있으며 의지가 없습니다. 또한 사용자가 지정하지 않았기 때문에 참고 signed char하거나 unsigned char, 컴파일러는 '일반'치료 여부를 문서를 보지 않고 말할 수 없다 char서명 또는 부호로 비트 필드에, 그리고 결정은 (이론적으로) 여부에 대한 결정 다를 수 있습니다 'plain' char은 비트 필드 외부에서 사용될 때 부호가 있거나 부호가 없습니다.
Jonathan Leffler

3
특히, C99에서 §6.7.2.1 구조체와 조합 지시자는 ¶4 A는 비트 필드는의하거나 규정 버전 타입 가진다 _Bool, signed int, unsigned int, 또는 몇몇 다른 구현 정의 타입. char따라서 사용 은 '기타 구현 정의 유형'범주에 속합니다.
Jonathan Leffler 2014

답변:


95

char필드의 기본 유형 으로 사용 하고 있기 때문에 컴파일러는 비트를 바이트 단위로 그룹화하려고 시도하고 각 바이트에 8 비트 이상을 넣을 수 없기 때문에 바이트 당 2 개의 필드 만 저장할 수 있습니다.

구조체에서 사용하는 총 비트 합계는 15이므로 많은 데이터에 맞는 이상적인 크기는 short.

#include <stdio.h>

typedef struct
{
  char A:3;
  char B:3;
  char C:3;
  char D:3;
  char E:3;
} col; 


typedef struct {
  short A:3;
  short B:3;
  short C:3;
  short D:3;
  short E:3;
} col2; 


int main(){

  printf("size of col: %lu\n", sizeof(col));
  printf("size of col2: %lu\n", sizeof(col2));

}

위의 코드 (나와 같은 64 비트 플랫폼의 경우)는 실제로 2두 번째 구조체에 대해 양보 할 것 입니다. a보다 큰 경우 short구조체는 사용 된 유형의 요소를 하나만 채울 것이므로 동일한 플랫폼의 경우 구조체는 크기가 4 인 경우 int, 8 인 long경우 등이됩니다.


1
제안 된 구조체 정의가 여전히 잘못되었습니다. 올바른 구조체 정의는 'unsigned short'를 사용합니다.
user3629249 2014

21
@ user3629249 서명되지 않은 짧은 '올바른'이유는 무엇입니까? 사용자가 -4에서 3까지 저장하려면 short가 맞습니다. 사용자가 0에서 7까지 저장하려는 경우 unsigned short가 정확합니다. 원래 질문은 서명 된 유형을 사용했지만 의도적 인 것인지 우연적인 것인지 구분할 수 없습니다.
Bruce Dawson

2
char과 사이에 차이점이있는 이유는 무엇 short입니까?
GingerPlusPlus 2014

5
@BruceDawson : 표준 허가 구현은 것으로 char서명되지 않은 ...
토마스 대한 수정 사항

@ThomasEding 사실, 표준은 char이 서명되지 않도록 허용합니다. 그러나 내 요점은 서명되지 않은 단락이 옳다고 주장 할 이유가 없다는 것입니다 ( 보통 그렇 겠지만).
Bruce Dawson

78

최소 정렬 경계 (1 바이트)에 걸쳐있는 비트 패킷 필드를 가질 수 없으므로 아마도 다음과 같이 압축 될 것입니다.

byte 1
  A : 3
  B : 3
  padding : 2
byte 2
  C : 3
  D : 3
  padding : 2
byte 3
  E : 3
  padding : 5

(동일한 바이트 내의 필드 / 패딩 순서는 의도적이지 않습니다. 컴파일러가 선호하는 방식을 배치 할 수 있으므로 아이디어를 제공하기위한 것입니다.)


16

처음 두 비트 필드는 단일 char. 세 번째는 그것에 맞지 않고 char새로운 것이 필요합니다. 3 + 3 + 3 = 9는 8 비트 문자에 맞지 않습니다.

따라서 첫 번째 쌍은 a를 char, 두 번째 쌍은 a를 char, 마지막 비트 필드는 세 번째를 얻습니다 char.


15

대부분의 컴파일러를 사용하면 패딩을 제어 할 수 있습니다 ( 예 : #pragmas 사용) . 다음은 GCC 4.8.1의 예입니다.

#include <stdio.h>

typedef struct
{
    char A:3;
    char B:3;
    char C:3;
    char D:3;
    char E:3;
} col;

#pragma pack(push, 1)
typedef struct {
    char A:3;
    char B:3;
    char C:3;
    char D:3;
    char E:3;
} col2;
#pragma pack(pop)

int main(){
    printf("size of col: %lu\n", sizeof(col));  // 3
    printf("size of col2: %lu\n", sizeof(col2));  // 2
}

컴파일러의 기본 동작은 이유가 있으며 아마도 더 나은 성능을 제공 할 것입니다.


9

ANSI C 표준은 "컴파일러가 비트 필드를 패킹 할 수 있지만 적합하다고 판단되는 경우"에 비해 상당한 이점을 제공하기 위해 비트 필드가 패킹되는 방법에 대해 너무 적게 지정하지만, 그럼에도 불구하고 많은 경우 컴파일러가 가장 효율적인 방식으로 패킹하는 것을 금지합니다.

특히 구조에 비트 필드가 포함 된 경우 컴파일러는이를 "일반"저장 유형의 하나 이상의 익명 필드를 포함하는 구조로 저장 한 다음 이러한 필드를 구성 비트 필드 부분으로 논리적으로 세분화해야합니다. 따라서 주어진 :

unsigned char foo1: 3;
unsigned char foo2: 3;
unsigned char foo3: 3;
unsigned char foo4: 3;
unsigned char foo5: 3;
unsigned char foo6: 3;
unsigned char foo7: 3;

경우 unsigned char8 비트, 컴파일러는 해당 유형의 네 개의 필드를 할당하고의 (a 될 것이다 제외한 모든 하나에 두 개의 비트 필드를 지정해야 할 것입니다 char자신의 필드). 모든 char선언이로 대체 된 경우 short유형의 필드가 두 개있을 것 short입니다. 그 중 하나는 5 개의 비트 필드를 보유하고 다른 하나는 나머지 2 개를 보유합니다.

정렬 제한이없는 프로세서 unsigned short에서는 처음 5 개 필드와 unsigned char마지막 2 개 필드를 사용하여 3 바이트에 7 개의 3 비트 필드를 저장 함으로써 데이터를보다 효율적으로 배치 할 수 있습니다 . 8 개의 3 비트 필드를 3 바이트로 저장할 수 있어야하지만 컴파일러는 "외부 필드"유형으로 사용할 수있는 3 바이트 숫자 유형이있는 경우에만이를 허용 할 수 있습니다.

개인적으로 나는 기본적으로 쓸모없는 것으로 정의 된 비트 필드를 고려합니다. 코드가 이진으로 압축 된 데이터로 작업해야하는 경우 실제 유형의 저장 위치를 ​​명시 적으로 정의한 다음 매크로 또는 기타 이러한 수단을 사용하여 비트에 액세스해야합니다. C가 다음과 같은 구문을 지원한다면 도움이 될 것입니다.

unsigned short f1;
unsigned char f2;
union foo1 = f1:0.3;
union foo2 = f1:3.3;
union foo3 = f1:6.3;
union foo4 = f1:9.3;
union foo5 = f1:12.3;
union foo6 = f2:0.3;
union foo7 = f2:3.3;

이러한 구문은 허용되는 경우 코드가 단어 크기 나 바이트 순서에 관계없이 이식 가능한 방식으로 비트 필드를 사용할 수 있도록합니다 (foo0은 f1의 최하위 비트 3 개에 속하지만 더 낮거나 더 높은 주소). 그러나 이러한 기능이 없으면 매크로가 이러한 작업을 수행하는 유일한 이식 방법 일 수 있습니다.


2
컴파일러마다 비트 필드를 다르게 배치합니다. Visual C ++에서 관련이있을 수있는 방법에 대한 문서를 작성했습니다. 그것은 몇 가지 성가신 함정을 지적
Bruce Dawson

글쎄, 당신은 일반 유형의 저장소와 동등한 것을 말하고 있으며 관심있는 단일 변수를 달성 하고이 메커니즘을 단순화하기 위해 일부 매크로를 사용하는 비트 필드 연산자를 사용합니다. C / C ++로 생성 된 코드도 이와 같은 작업을 수행한다고 생각합니다. 구조체를 사용하는 것은 단지 "더 나은"코드 구성을위한 것이며 실제로는 전혀 필요하지 않습니다.
Raffaello 2014
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.