2014-11-02 4 views
90

나는이 구조체 정의 :이 구조체 크기가 2가 아닌 이유는 무엇입니까?

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

sizeof(col) 나에게 (3)의 출력을 제공을하지만, 2이어야한다? 만약 내가 단지 하나의 요소에 대해 언급하면 ​​sizeof은 2입니다. 왜 그런지 이해하지 못합니다 : 3 비트의 5 요소는 15 비트와 같으며 2 바이트보다 작습니다.

이와 같은 구조를 정의하는 데 "내부 크기"가 있습니까? 지금까지 언어에 대한 내 생각으로는 3 바이트가 아닌 2 바이트의 크기를 예상했기 때문에 설명이 필요합니다.

+4

아마 정렬 최적화 일 것입니다. 다음 비트 크기가 실제 점유 공간에 맞지 않으면 새로운 바이트를 시작합니다. –

+4

비트 패킹이 필요한 외부 제약 조건이 없거나 플랫폼이 표준이 제공하는 것보다 몇 가지 추가 보장을 제공하지 않는 한 비트 필드를 사용하는 것이 거의 없습니다. –

+3

C의 경우 char을 사용하면 int를 사용하는 것보다 이식성이 떨어집니다 (http://stackoverflow.com/a/23987436/23118). – hlovdal

답변

94

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

구조체에서 사용하는 총 비트 수는 15이므로 많은 데이터를 저장하기에 적합한 크기는 short입니다. 실제로 제 구조체에 대한 2을 수득한다

#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 비트 플랫폼) 위의 코드. 같은 플랫폼 - - short보다 큰 것을 위해, 구조체가 사용 된 타입의 두 개 이상의 요소를 채우기, 그래서 것이다 구조체, int에 대한 크기 네 가지로 long 8 개를 끝날 것 등

+1

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

+21

@ user3629249 부호없는 짧은 '올바른'이유는 무엇입니까? 사용자가 -4에서 3으로 저장하기를 원하는 경우 짧음이 정확합니다. 사용자가 0에서 7까지 저장하고자한다면 unsigned short가 정확하다. 원래 질문은 서명 된 유형을 사용했지만 의도적인지 실수인지는 알 수 없습니다. –

+2

'char'와'short'의 차이점은 무엇입니까? – GingerPlusPlus

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 비트 문자에 맞지 않습니다.

첫 번째 쌍은 char이고 두 번째 쌍은 char이며 마지막 비트 필드는 세 번째 char입니다.

9

을하더라도 ANSI C 표준은 "컴파일러가 적합하다고 생각하는 비트 필드를 패키징 할 수 있지만"많은 경우 컴파일러가 가장 효율적인 방식으로 항목을 패키징하는 것을 금지합니다.

특히, 구조체에 비트 필드가 포함되어있는 경우 컴파일러는 하나 이상의 익명 필드를 포함하는 구조로 저장 한 다음 이러한 각 필드를 구성 비트 필드 부분으로 논리적으로 세분화해야합니다. 따라서, 주어진 : unsigned char는 8 비트입니다

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; 

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

정렬 제한이없는 프로세서의 경우 첫 번째 5 개 필드는 unsigned short, 마지막 두 개 필드는 unsigned char을 사용하여 7 개의 3 비트 필드를 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++이 어떻게 관련이 있는지에 대한 몇 가지 문서를 작성했습니다. 귀찮은 함정 중 일부를 지적합니다 : https://randomascii.wordpress.com/2010/06/06/bit-field-packing-with-visual-c/ –

+0

글쎄, 당신은 일반 유형 및 비트 필드 연산자를 사용하여 관심있는 단일 변수를 달성하고이 메커니즘을 단순화하려면 일부 매크로를 사용하십시오. 나는 c/C++에서 생성 된 코드가 이와 비슷한 것을하고 있다고 생각한다. 구조체를 사용하는 것은 코드의 "더 나은"조직을위한 것일뿐 실제로 필요하지는 않습니다. – Raffaello

15

대부분의 컴파일러를 사용하면 패딩을 제어 할 수 있습니다 (e.g. using #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 
} 

컴파일러의 기본 동작은 이유가 있기 때문에 더 나은 성능을 제공합니다.

관련 문제