2017-10-31 8 views
0

나는처럼 보이는 비트 필드가 다음비트 필드 값이 변경 소켓 C++를 통해 전송 될 때

typedef struct __attribute__((__packed__)) MyStruct { 
    unsigned int val1:14; 
    unsigned int val2:1; 
    unsigned int val3:1; 
    unsigned int val4:1; 
    unsigned int val5:1; 
    unsigned short aFullShort; 
    unsigned int aFullInt; 
} MyStruct; 

내가 네트워크를 통해이 값을 보내고, 때로는 보낸 사람이 (val1가 설정되어 있다고 생각됩니다 것으로 나타났습니다를 보내기 전에 값을 인쇄하여 확인할 수 있음) 수신자는 val1이 설정되어 있는지 확인하지 않습니다. 다음과 같이 전송을위한 코드는 다음과 같습니다

MyStruct* myStruct = new MyStruct(); //initialize fields here 
sendto(sock, myStruct, sizeof(MyStruct), 0, ...); 

다음과 독서에 대한 코드는 다음과 같습니다

unsigned char theBuffer[sizeof(MyStruct)]; 
recvfrom(aSocket, &theBuffer, sizeof(theBuffer), 0, ...); 

소켓에서 바이트 읽은 후, 나는 MyStruct에 바이트를 캐스팅 엔디안 수행 재 해석 aFullShortaFullInt에 대한 변환. 보낸 사람이 1로 설정하면 수신자가 val1이 0이라고 생각하는 손상이 발생합니다. 왜 이런 일이 발생할 수 있습니까? 컴파일러가 발신자와 수신자에 대해 다른 패딩을 삽입 할 수 있습니까? 단일 비트 값의 엔디안에 대해 걱정할 필요가 있습니까?

+1

발신자/수신자가 동일한 OS 및 동일한 컴파일러입니다. 비트 필드 레이아웃은 구현이 정의됩니다. –

+0

@RichardCritten 보낸 사람과받는 사람이 동일한 호스트에서 실행되고 동일한 컴파일러를 사용하여 컴파일됩니다. 송신자와 수신자에 대해 두 개의 별도 바이너리가 작성되므로 컴파일러가 두 대상에 대해 서로 다른 최적화를 수행 할 수 있습니다. – user1832287

+0

송신자에서'(char *) (void *) myStruct'를 덤프하고 수신자에서'theBuffer'를 덤프합니다. 바이트가 같습니까? 그렇다면 구조적 차이점을 지적합니다. – ikegami

답변

2

컴파일러는 비트 필드를 원하는대로 배치 할 수 있습니다. 원하는 경우 모든 실행에 대해 무작위로 선택할 수 있습니다. 이것을 금지하는 규칙은 없습니다. 의존 할 수있는 예측 가능한 바이너리 형식으로 데이터를 직렬화하려면 해당 작업을 수행하는 코드를 작성하십시오.

유일한 예외는 컴파일러가 압축 된 구조체에 대해 명시된 보증을 가지고 있고 해당 컴파일러에만 자신을 한정하려는 경우입니다. 당신이 사용하고있는 컴파일러를 지정하지 않았지만, 그렇다고는 생각하지 않습니다.

이렇게 코드를 작성할 이유가 없습니다. 관련 표준에 의해 작동하도록 보장 된 코드를 작성하고, 가정이 깨지면 작동하지 않을 코드는 작성하지 말아야합니다.

+0

비트 필드 레이아웃은 구현에 따라 다르지만 일관성이 있어야합니다. 파일에 비트 필드를 쓰고 나중에 동일한 구현으로 파일을 읽으면 동일한 값을 다시 얻어야합니다. 그것은 무작위 적이 될 수 없습니다. – Barmar

+0

@Barmar 왜 그렇게 말합니까? 나는 그것이 당신이 일관성있게 일어날 것이라고 기대하는 이유를 이해합니다. 그러나 "일관성을 유지해야한다"고 생각하는 이유는 무엇입니까? C++ 표준은 그렇게 말합니까? POSIX가 있습니까? 귀하의 컴파일러 설명서가 있습니까? –

+0

이 아닌 경우 비트 필드를 별도의 컴파일 단위에있는 통신 사이에 사용할 수 없으므로 라이브러리 등의 인수에 사용할 수 없습니다. – Barmar