문제

구조물 내에서 비트 필드의 순서는 플랫폼에 따라 다르다는 것을 읽었습니다. 다른 컴파일러 별 패킹 옵션을 사용하면이 보증 데이터가 작성된대로 적절한 순서로 저장됩니까? 예를 들어:

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를 통해 Big Endian Machines는 Big End First와 Little Endian Machines가 Bits Little End를 먼저 배치했습니다.

K & R은 "구조의 인접한 [BIT-] 필드 멤버는 구현 의존적 방향으로 구현 의존적 저장 장치에 포장됩니다. 다른 필드를 따르는 필드가 맞지 않으면 ... 장치 사이에 분할 될 수 있습니다. 패딩. 무명 너비의 필드 0이 패딩을 강요 ... "

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

이 마지막 진술은 패딩으로 인해 비 비트 필드에도 적용됩니다. 그러나 모든 컴파일러는 이미 GCC를 위해 발견 한 것처럼 보이기 때문에 구조물의 바이트 포장을 강제하는 방법을 가지고있는 것 같습니다.

비트 필드를 피해야합니다. 동일한 플랫폼에서도 컴파일러간에 휴대가 가능하지 않습니다. C99 표준 6.7.2.1/10- "구조 및 노조 지정자"(C90 표준에 유사한 문구가 있습니다)에서 :

구현은 비트 필드를 보유 할만 큼 큰 주소가 가능한 저장 장치를 할당 할 수 있습니다. 충분한 공간이 남아있는 경우, 구조물의 다른 비트 필드를 즉시 따르는 비트 필드는 동일한 장치의 인접한 비트로 포장되어야합니다. 공간이 충분하지 않은 경우, 맞지 않는 비트 필드가 다음 장치에 넣거나 인접한 단위가 겹치는지 여부는 구현 정의됩니다. 장치 내에서 비트 필드 할당 순서 (고차 또는 저차 또는 저차에서 고차로 저격자)가 구현되지 않았습니다. 주소 수성 저장 장치의 정렬은 지정되지 않습니다.

비트 필드가 INT 경계를 '스팬'하는지 여부를 보장 할 수 없으며 비트 필드가 INT의 낮은 엔드 또는 INT의 하이 엔드에서 시작하는지 여부를 지정할 수 없습니다 (이것은 프로세서가 있는지 여부와 독립적입니다. 빅 엔디 안 또는 리틀 엔디언).

비트 마스크를 선호합니다. 인라인 (또는 매크로)을 사용하여 비트를 설정하고 지우고 테스트하십시오.

Endianness는 비트 주문이 아닌 바이트 주문에 대해 이야기하고 있습니다. 요즘 , 비트 주문이 고정되어 있다고 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

아마도 대부분은 아마도 농장에 베팅하지 않을 것입니다. 왜냐하면 당신이 틀렸다면 크게 잃을 것입니다.

실제로 동일한 바이너리 정보를 가져야한다면 비트 마스크가있는 비트 필드를 만들어야합니다. 예를 들어 메시지를 위해 서명되지 않은 짧은 짧은 (16 비트)를 사용한 다음 버전 매스크 = 0xe000과 같은 것을 3 개의 최상위 비트를 나타냅니다.

structs 내에서 정렬과 비슷한 문제가 있습니다. 예를 들어, SPARC, PowerPC 및 680x0 CPU는 모두 빅 엔디 안이며 SPARC 및 PowerPC 컴파일러의 공통 기본값은 4 바이트 경계에서 구조 부재를 정렬하는 것입니다. 그러나 680x0에 사용한 하나의 컴파일러는 2 바이트 경계에만 정렬되었습니다. 정렬을 변경할 수있는 옵션이 없었습니다!

따라서 일부 structs의 경우 SPARC 및 PowerPC의 크기는 동일하지만 680x0에서는 작고 일부 부재는 구조물 내에서 다른 메모리 오프셋에 있습니다.

SPARC에서 실행되는 서버 프로세스가 클라이언트를 쿼리하고 Big-Endian이라는 것을 알게되며 네트워크에서 이진 스트러크를 분출 할 수 있다고 가정하고 클라이언트가 대처할 수 있다고 가정하기 때문에 이것은 내가 작업 한 한 프로젝트의 문제였습니다. 그리고 그것은 PowerPC 클라이언트에서 잘 작동했으며 680x0 클라이언트에서 큰 타임으로 추락했습니다. 나는 코드를 작성하지 않았고 문제를 찾는 데 꽤 오랜 시간이 걸렸습니다. 그러나 내가 한 후에는 쉽게 해결하기가 쉬웠습니다.

물론 가장 좋은 대답은 비트 필드를 스트림으로 읽거나 쓰는 클래스를 사용하는 것입니다. C 비트 필드 구조를 사용하는 것은 보장되지 않습니다. 말할 것도없이, 실제 세계 코딩에서 이것을 사용하는 것은 비전문가/게으른/바보로 간주됩니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top