Nice programing

C / C ++ : 강제 비트 필드 순서 및 정렬

nicepro 2020. 10. 4. 13:25
반응형

C / C ++ : 강제 비트 필드 순서 및 정렬


구조체 내 비트 필드의 순서는 플랫폼에 따라 다릅니다. 다른 컴파일러 별 패킹 옵션을 사용하면 데이터가 기록 될 때 적절한 순서로 저장된다는 보장이 있습니까? 예를 들면 :

struct Message
{
  unsigned int version : 3;
  unsigned int type : 1;
  unsigned int id : 5;
  unsigned int data : 6;
} __attribute__ ((__packed__));

GCC 컴파일러가있는 인텔 프로세서에서 필드는 표시된대로 메모리에 배치되었습니다. Message.version버퍼의 처음 3 비트이고 Message.type그 뒤를 따릅니다. 다양한 컴파일러에 대해 동등한 구조체 패킹 옵션을 찾으면 크로스 플랫폼입니까?


아니요, 완전히 휴대 할 수 없습니다. 구조체에 대한 패킹 옵션은 확장이며 그 자체로는 완전히 이식 할 수 없습니다. 또한 C99 §6.7.2.1, 단락 10에서는 "단위 내에서 비트 필드 할당 순서 (상위에서 낮은 순서로 또는 낮은 순서에서 높은 순서로)는 구현에 따라 정의됩니다."라고 말합니다.

예를 들어, 단일 컴파일러조차도 대상 플랫폼의 엔디안성에 따라 비트 필드를 다르게 배치 할 수 있습니다.


비트 필드는 컴파일러마다 매우 다양합니다. 죄송합니다.

GCC를 사용하면 빅 엔디안 머신은 비트 빅 엔드를 먼저 배치하고 리틀 엔디안 머신은 비트를 리틀 엔드 우선 배치합니다.

K & R은 "구조의 인접한 [비트] 필드 멤버는 구현 종속적 인 방향으로 구현 종속적 인 저장 단위로 압축됩니다. 다른 필드를 따르는 필드가 맞지 않을 때 ... 단위로 분할되거나 단위가 될 수 있습니다. 패딩 됨. 너비가 0 인 이름없는 필드는이 패딩을 강제합니다 ... "

따라서 기계 독립적 인 바이너리 레이아웃이 필요한 경우 직접 수행해야합니다.

이 마지막 진술은 패딩으로 인해 비트 필드가 아닌 경우에도 적용됩니다. 그러나 모든 컴파일러는 이미 GCC에 대해 발견 한 것처럼 구조의 바이트 패킹을 강제하는 방법을 가지고있는 것 같습니다.


비트 필드는 피해야합니다. 동일한 플랫폼에서도 컴파일러간에 이식성이 매우 낮습니다. C99 표준 6.7.2.1/10에서- "구조 및 공용체 지정자"(C90 표준에 유사한 표현이 있음) :

구현은 비트 필드를 보유하기에 충분히 큰 어 드레서 블 저장 유닛을 할당 할 수 있습니다. 충분한 공간이 남아있는 경우 구조에서 다른 비트 필드 바로 뒤에 오는 비트 필드는 동일한 단위의 인접 비트로 패킹됩니다. 공간이 충분하지 않은 경우 적합하지 않은 비트 필드가 다음 유닛에 들어가거나 인접한 유닛과 겹치는 지 여부는 구현에서 정의됩니다. 단위 내 비트 필드 할당 순서 (상위에서 하위로 또는 하위에서 상위로)는 구현에 따라 정의됩니다. 주소 지정 가능한 저장 장치의 정렬이 지정되지 않았습니다.

비트 필드가 int 경계를 '스팬'할지 여부를 보장 할 수 없으며 비트 필드가 int의 낮은 끝에서 시작하는지 아니면 높은 끝에서 시작하는지 지정할 수 없습니다 (이는 프로세서가 빅 엔디안 또는 리틀 엔디안).

비트 마스크를 선호합니다. 인라인 (또는 매크로)을 사용하여 비트를 설정, 삭제 및 테스트합니다.


엔디안은 비트 순서가 아닌 바이트 순서에 대해 이야기합니다. 요즘에는 비트 오더가 고정되어 있다고 99 % 확신합니다. 그러나 비트 필드를 사용할 때는 엔디안을 고려해야합니다. 아래 예를 참조하십시오.

#include <stdio.h>

typedef struct tagT{

    int a:4;
    int b:4;
    int c:8;
    int d:16;
}T;


int main()
{
    char data[]={0x12,0x34,0x56,0x78};
    T *t = (T*)data;
    printf("a =0x%x\n" ,t->a);
    printf("b =0x%x\n" ,t->b);
    printf("c =0x%x\n" ,t->c);
    printf("d =0x%x\n" ,t->d);

    return 0;
}

//- big endian :  mips24k-linux-gcc (GCC) 4.2.3 - big endian
a =0x1
b =0x2
c =0x34
d =0x5678
 1   2   3   4   5   6   7   8
\_/ \_/ \_____/ \_____________/
 a   b     c           d

// - little endian : gcc (Ubuntu 4.3.2-1ubuntu11) 4.3.2
a =0x2
b =0x1
c =0x34
d =0x7856
 7   8   5   6   3   4   1   2
\_____________/ \_____/ \_/ \_/
       d           c     b   a

대부분의 경우 아마도 농장에 베팅하지 마십시오. 틀리면 크게 잃을 것입니다.

정말로 동일한 이진 정보가 필요한 경우 비트 마스크로 비트 필드를 만들어야합니다. 예를 들어 메시지에 대해 unsigned short (16 비트)를 사용한 다음 versionMask = 0xE000과 같은 것을 만들어 최상위 비트 3 개를 나타냅니다.

There's a similar problem with alignment within structs. For instance, Sparc, PowerPC, and 680x0 CPUs are all big-endian, and the common default for Sparc and PowerPC compilers is to align struct members on 4-byte boundaries. However, one compiler I used for 680x0 only aligned on 2-byte boundaries - and there was no option to change the alignment!

So for some structs, the sizes on Sparc and PowerPC are identical, but smaller on 680x0, and some of the members are in different memory offsets within the struct.

This was a problem with one project I worked on, because a server process running on Sparc would query a client and find out it was big-endian, and assume it could just squirt binary structs out on the network and the client could cope. And that worked fine on PowerPC clients, and crashed big-time on 680x0 clients. I didn't write the code, and it took quite a while to find the problem. But it was easy to fix once I did.


Thanks @BenVoigt for your very useful comment starting

No, they were created to save memory.

Linux source does use a bit field to match to an external structure: /usr/include/linux/ip.h has this code for the first byte of an IP datagram

struct iphdr {
#if defined(__LITTLE_ENDIAN_BITFIELD)
        __u8    ihl:4,
                version:4;
#elif defined (__BIG_ENDIAN_BITFIELD)
        __u8    version:4,
                ihl:4;
#else
#error  "Please fix <asm/byteorder.h>"
#endif

However in light of your comment I'm giving up trying to get this to work for the multi-byte bit field frag_off.


Of course the best answer is to use a class which reads/writes bit fields as a stream. Using the C bit field structure is just not guaranteed. Not to mention it is considered unprofessional/lazy/stupid to use this in real world coding.

참고URL : https://stackoverflow.com/questions/1490092/c-c-force-bit-field-order-and-alignment

반응형