programing

C 또는 C++ 구조에 대한 특정 엔디안을 강제할 방법이 있습니까?

closeapi 2023. 9. 18. 21:25
반응형

C 또는 C++ 구조에 대한 특정 엔디안을 강제할 방법이 있습니까?

저는 구조물의 고유성에 관한 몇 가지 질문과 답변을 보았지만, 그것들은 시스템의 고유성을 감지하거나 두 개의 다른 고유성 사이에서 데이터를 변환하는 것에 관한 것이었습니다.

그러나 주어진 구조의 특정한 종말성을 강제할 수 있는 방법이 있다면 지금 하고 싶은 것은 무엇입니까?비트 필드에서 조작하는 수많은 매크로에서 전체를 다시 쓰는 것 외에 좋은 컴파일러 지시나 다른 간단한 해결책이 있습니까?

일반적인 솔루션이 좋겠으나, 특정 gcc 솔루션도 있으면 좋겠습니다.

편집:

왜 엔디안을 강요하는 것이 좋지 않은지 지적해주신 모든 의견에 감사드립니다만, 제 경우에는 그게 바로 제게 필요한 것입니다.

많은 양의 데이터가 특정 프로세서에 의해 생성되며(이 프로세서는 사용자 지정 하드웨어가 있는 임베디드 시스템이므로 변경되지 않음), 알 수 없는 프로세서에서 실행 중인 프로그램(작업 중임)을 통해 데이터를 읽어야 합니다.데이터를 바이트 단위로 평가하는 것은 엄청나게 번거로울 것입니다. 왜냐하면 데이터는 크고 깊은 수백 가지 유형의 구조물로 구성되어 있기 때문입니다. 대부분의 구조물은 내부에 다른 거대한 구조물의 층이 많이 있기 때문입니다.

임베디드 프로세서의 소프트웨어를 변경하는 것은 불가능합니다.소스를 사용할 수 있기 때문에 처음부터 시작하여 바이트 단위로 모든 데이터를 평가하는 대신 해당 시스템의 구조를 사용하려고 합니다.

이것이 컴파일러에게 어떤 엔디안을 사용해야 하는지 알려줘야 하는 이유입니다. 아무리 효율적이든 아니든 상관없습니다.

그것은 진정한 엔디안의 변화일 필요는 없습니다.그것이 단지 인터페이스일 뿐이고 물리적으로 모든 것이 프로세서 고유의 엔디안에서 처리된다고 해도, 저는 그것을 완벽하게 받아들일 수 있습니다.

파티에 조금 늦었지만 현재의 GCC(작동하는 곳은 6.2.1에서 테스트되었고 구현되지 않는 곳은 4.9.2에서 테스트됨)를 통해 구조를 X-endian 바이트 순서로 유지해야 한다고 선언할 수 있는 방법이 마침내 있습니다.

다음 테스트 프로그램:

#include <stdio.h>
#include <stdint.h>

struct __attribute__((packed, scalar_storage_order("big-endian"))) mystruct {
    uint16_t a;
    uint32_t b;
    uint64_t c;
};


int main(int argc, char** argv) {
    struct mystruct bar = {.a = 0xaabb, .b = 0xff0000aa, .c = 0xabcdefaabbccddee};

    FILE *f = fopen("out.bin", "wb");
    size_t written = fwrite(&bar, sizeof(struct mystruct), 1, f);
    fclose(f);
}

는 hex 편집기(예: hexdump -Cout.bin)로 검사할 수 있는 "out.bin" 파일을 만듭니다.skalar_storage_order 특성이 지원되는 경우 홀이 없는 이 순서로 예상되는 0xaabbff00aabdefaabccddee가 포함됩니다.슬프게도 이것은 매우 구체적인 컴파일러입니다.

제가 주로 다루는 방식은 이렇습니다.

#include <arpa/inet.h> // for ntohs() etc.
#include <stdint.h>

class be_uint16_t {
public:
        be_uint16_t() : be_val_(0) {
        }
        // Transparently cast from uint16_t
        be_uint16_t(const uint16_t &val) : be_val_(htons(val)) {
        }
        // Transparently cast to uint16_t
        operator uint16_t() const {
                return ntohs(be_val_);
        }
private:
        uint16_t be_val_;
} __attribute__((packed));

에 대해서도 be_uint32_t.

그런 다음 구조를 다음과 같이 정의할 수 있습니다.

struct be_fixed64_t {
    be_uint32_t int_part;
    be_uint32_t frac_part;
} __attribute__((packed));

요점은 컴파일러가 거의 확실하게 당신이 쓰는 순서대로 필드를 배치할 것이라는 것입니다. 그래서 당신이 정말로 걱정하는 것은 빅 엔디언 정수입니다.be_uint16_t에서 투명하게 을 알고 입니다.object는 big-endian과 machine-endian 사이에서 투명하게 변환하는 방법을 알고 있습니다.다음과 같은 경우:

be_uint16_t x = 12;
x = x + 1; // Yes, this actually works
write(fd, &x, sizeof(x)); // writes 13 to file in big-endian form

사실, 만약 당신이 그 토막글을 꽤 좋은 C++ 컴파일러와 함께 컴파일한다면, 당신은 그것이 상수로서 빅 엔디언 "13"을 방출한다는 것을 발견해야 합니다.

이러한 객체를 사용하면 메모리 내 표현은 빅 엔디안(big-endian)이 됩니다.따라서 배열을 생성하고 구조물에 배치하는 등의 작업을 수행할 수 있습니다.하지만 수술하러 가면 마법처럼 기계 엔디안에 캐스팅됩니다.이것은 일반적으로 x86의 단일 명령어이므로 매우 효율적입니다.손으로 직접 캐스팅해야 하는 몇 가지 상황이 있습니다.

be_uint16_t x = 37;
printf("x == %u\n", (unsigned)x); // Fails to compile without the cast

...대부분의 코드에서는 내장된 유형인 것처럼 사용할 수 있습니다.

를 사용해 .
#pragma scalar_storage_order big-endian빅 형식으로 빅언다로다n로g빅oe언-td
#pragma scalar_storage_order little-endian 엔디안에
#pragma scalar_storage_order default를 사용합니다.

여기서 자세히 보기

아니요, 저는 그렇게 생각하지 않습니다.

Endianness는 정수가 왼쪽에서 오른쪽으로 표현되는지 또는 오른쪽에서 왼쪽으로 표현되는지를 나타내는 프로세서의 속성입니다. 컴파일러의 속성이 아닙니다.

당신이 할 수 있는 최선의 방법은 어떤 바이트 순서와도 무관한 코드를 쓰는 것입니다.

아니요, 그런 능력은 없습니다.C++가 지원하지 않도록 컴파일러가 과도하거나 비효율적인 코드를 생성해야 할 수 있는 상황이 존재한다면 말입니다.

직렬화를 처리하는 일반적인 C++ 방식은 원하는 정확한 레이아웃에서 구조를 메모리에 유지하고 역직렬화 시 엔디안성이 보존되는 방식으로 직렬화를 수행하는 것입니다.

아래 내용들이 귀사의 목적에 맞게 수정될 수 있을지 잘 모르겠습니다만, 제가 일하는 곳에서는 아래 내용들이 많은 경우에 유용하다는 것을 알게 되었습니다.

엔디안니스가 중요할 때 우리는 두 가지 다른 데이터 구조를 사용합니다.하나는 그것이 어떻게 도착할 것인지를 나타내기 위해 행해집니다.다른 하나는 우리가 그것을 기억 속에 어떻게 표현하기를 바라는 것입니다.그런 다음 변환 루틴이 둘 사이를 전환하도록 개발됩니다.

워크플로우는 따라서 작동합니다...

  1. 데이터를 원시 구조로 읽어 들입니다.
  2. "원시 구조"를 "메모리 버전"으로 변환
  3. "메모리 내 버전"에서만 작동
  4. 작업이 완료되면 "in memory version"을 "raw structure"로 다시 변환하여 작성합니다.

우리는 이 분리가 유용하다고 생각합니다. 왜냐하면 (이에 국한되지는 않습니다.)

  1. 모든 변환은 한 곳에서만 수행됩니다.
  2. "in memory version"으로 작업할 때 메모리 정렬 문제로 인한 두통이 줄어듭니다.
  3. 한 아치에서 다른 아치로 포팅하는 것이 훨씬 쉬워집니다(엔디안 문제가 적습니다).

이 디커플링이 응용 프로그램에도 유용하게 사용될 수 있기를 바랍니다.

가능한 혁신적인 해결책은 엔디언 코딩을 크게 하는 것과 같은 C 인터프리터를 사용하는 것입니다.

부스트는 이를 위한 엔디언 버퍼를 제공합니다.

예를 들어,

#include <boost/endian/buffers.hpp>
#include <boost/static_assert.hpp>

using namespace boost::endian;

struct header {
    big_int32_buf_t     file_code;
    big_int32_buf_t     file_length;
    little_int32_buf_t  version;
    little_int32_buf_t  shape_type;
};
BOOST_STATIC_ASSERT(sizeof(h) == 16U);

직접적인 답은 아니지만, 이 질문을 까지 읽는 것이 여러분의 걱정에 대한 답이 될 수 있기를 바랍니다.

구조를 데이터 멤버에 대한 게터와 세터가 있는 클래스로 만들 수 있습니다.게터와 세터는 다음과 같은 것으로 구현됩니다.

int getSomeValue( void ) const {
#if defined( BIG_ENDIAN )
    return _value;
#else
    return convert_to_little_endian( _value );
#endif
}

void setSomeValue( int newValue) {
#if defined( BIG_ENDIAN )
    _value = newValue;
#else
    _value = convert_to_big_endian( newValue );
#endif
}

우리는 때때로 파일에서 구조물을 읽을 때 이 작업을 수행합니다. 우리는 구조물을 읽고 데이터에 제대로 액세스하기 위해 빅 엔디안 머신과 리틀 엔디안 머신 모두에서 이 작업을 사용합니다.

XDR이라고 하는 데이터 표현이 있습니다.한번 보세요.http://en.wikipedia.org/wiki/External_Data_Representation

Embedded System에는 조금 무리가 될 수도 있습니다.사용할 수 있는 이미 구현된 라이브러리를 검색해 봅니다(라이선스 제한 확인!).

XDR은 Endianness 독립적인 방식으로 데이터를 이동할 수 있는 방법이 필요하기 때문에 네트워크 시스템에서 일반적으로 사용됩니다.네트워크 밖에서 사용할 수 없다는 말은 없지만 말입니다.

언급URL : https://stackoverflow.com/questions/6732127/is-there-a-way-to-enforce-specific-endianness-for-a-c-or-c-struct

반응형