문제

C ++의 유형을 구성하는 포인터/어레이에 대한 컨벤션에 대해 궁금합니다. 현재 사용 케이스는 다음과 같습니다.

32 비트 정수의 배열로 취급하여 (총 길이가 4의 배수라는 것을 알고 있음) 모든 값을 합산하고 오버플로를 무시함으로써 이진 데이터의 이진 블로브를 통해 간단한 32 비트 체크섬을 계산합니다.

나는 그러한 기능이 다음과 같이 보일 것으로 기대합니다.

uint32_t compute_checksum(const char *data, size_t size)
{
    const uint32_t *udata = /* ??? */;
    uint32_t checksum = 0;
    for (size_t i = 0; i != size / 4; ++i)
        checksum += udata[i];
    return udata;
 }

이제 제가 가진 질문은, 당신이 "가장 좋은"방법을 어떻게 고려합니까? data 에게 udata?

C 스타일 캐스트?

udata = (const uint32_t *)data

모든 포인터가 컨버터블이라고 가정하는 C ++ 캐스트?

udata = reinterpret_cast<const uint32_t *>(data)

C ++는 중간체를 사용하여 임의의 포인터 유형 사이에 캐스트 void*?

udata = static_cast<const uint32_t *>(static_cast<const void *>(data))

노조를 통해 캐스팅?

union {
    const uint32_t *udata;
    const char *cdata;
};
cdata = data;
// now use udata

나는 이것이 100% 휴대용 솔루션이 아니라는 것을 완전히 알고 있지만, 그것이 작동하는 작은 플랫폼 세트 (즉, 정렬되지 않은 메모리 액세스 및 포인터 앨리어싱에 대한 컴파일러 가정)에서만 사용할 것으로 기대하고 있습니다. 어떤 것을 권 하시죠?

도움이 되었습니까?

해결책

C ++ 표준에 관한 한 litb의 대답은 완전히 정확하고 가장 휴대가 가능합니다. 주조 const char *data a const uint3_t *, C 스타일의 캐스트를 통해 여부에 관계없이 static_cast, 또는 reinterpret_cast, 엄격한 별칭 규칙을 중단합니다 (참조 엄격한 별칭 이해). 완전한 최적화로 컴파일하면 코드가 올바른 일이 될 가능성이 높습니다.

노조를 통해 캐스팅 (Litb와 같은 my_reint)는 아마도 최상의 솔루션 일 것입니다. 비록 당신이 한 회원을 통해 노동 조합에 글을 쓰고 다른 회원을 통해 그것을 읽는다면 기술적으로 규칙을 위반하지만 정의되지 않은 행동을 초래한다는 규칙을 기술적으로 위반합니다. 그러나 실제로 모든 컴파일러가이를 지원하며 예상 결과를 초래합니다. 표준 100%를 절대적으로 준수하고 싶다면 비트 시프트 방법으로 이동하십시오. 그렇지 않으면 노조를 통해 캐스팅을하는 것이 좋습니다. 이는 더 나은 성능을 제공 할 수 있습니다.

다른 팁

효율성 무시, 코드의 단순성을 위해 :

#include <numeric>
#include <vector>
#include <cstring>

uint32_t compute_checksum(const char *data, size_t size) {
    std::vector<uint32_t> intdata(size/sizeof(uint32_t));
    std::memcpy(&intdata[0], data, size);
    return std::accumulate(intdata.begin(), intdata.end(), 0);
}

나는 또한 Litb의 마지막 답변을 좋아합니다. Char가 서명 될 수 있기 때문에 추가 마스크가 필요하다고 생각합니다.

checksum += ((data[i] && 0xFF) << shift[i % 4]);

유형 Punning이 잠재적 인 문제 일 때, 나는 그것을 안전하게 시도하기보다는 pun을 입력하지 않는 것을 선호합니다. 처음에 별개의 유형의 별명 포인터를 만들지 않으면 컴파일러가 별명으로 무엇을 할 수 있는지 걱정할 필요가 없으며, 연합을 통해 여러 static_cast를 보는 유지 보수 프로그래머도 마찬가지입니다.

더 많은 메모리를 할당하고 싶지 않다면 :

uint32_t compute_checksum(const char *data, size_t size) {
    uint32_t total = 0;
    for (size_t i = 0; i < size; i += sizeof(uint32_t)) {
        uint32_t thisone;
        std::memcpy(&thisone, &data[i], sizeof(uint32_t));
        total += thisone;
    }
    return total;
}

충분한 최적화는 GCC에서 Memcpy와 추가 UINT32_T 변수를 완전히 제거하고 소스 배열에서 바로 플랫폼에서 가장 효율적인 방법이 무엇이든, 정수 값을 정렬하지 않은 정수 값을 읽을 수 있습니다. 다른 "심각한"컴파일러도 마찬가지입니다. 그러나이 코드는 이제 Litb보다 크기 때문에 UINT64_T에서도 잘 작동하는 기능 템플릿으로 바꾸는 것 외에는 내 말에 대해 말할 것이 많지 않으며, 광산은 조금 선택하지 않고 네이티브 엔디 어로 작동합니다. -엔디 안.

물론 이것은 완전히 휴대용이 아닙니다. 크기 (UINT32_T) char의 스토리지 표현은 우리가 원하는 방식으로 UIN32_T의 스토리지 표현에 해당한다고 가정합니다. 이것은 하나가 다른 사람으로 "취급 될 수있다"는 질문에 의해 암시된다. 숯이 8 비트인지, UINT32_T가 스토리지 표현에서 모든 비트를 사용하는지 여부는 분명히 침입 할 수 있지만 문제는 그렇지 않음을 암시합니다.

내 50 센트가 있습니다.

#include <iostream>
#include <string>
#include <cstring>

    uint32_t compute_checksum_memcpy(const char *data, size_t size)
    {
        uint32_t checksum = 0;
        for (size_t i = 0; i != size / 4; ++i)
        {
            // memcpy may be slow, unneeded allocation
            uint32_t dest; 
            memcpy(&dest,data+i,4);
            checksum += dest;
        }
        return checksum;
    }

    uint32_t compute_checksum_address_recast(const char *data, size_t size)
    {
        uint32_t checksum = 0;
        for (size_t i = 0; i != size / 4; ++i)
        {
            //classic old type punning
            checksum +=  *(uint32_t*)(data+i);
        }
        return checksum;
    }

    uint32_t compute_checksum_union(const char *data, size_t size)
    {
        uint32_t checksum = 0;
        for (size_t i = 0; i != size / 4; ++i)
        {
            //Syntax hell
            checksum +=  *((union{const char* c;uint32_t* i;}){.c=data+i}).i;
        }
        return checksum;
    }

    // Wrong!
    uint32_t compute_checksum_deref(const char *data, size_t size)
    {
        uint32_t checksum = 0;
        for (size_t i = 0; i != size / 4; ++i)
        {
            checksum +=  *&data[i];
        }
        return checksum;
    }

    // Wrong!
    uint32_t compute_checksum_cast(const char *data, size_t size)
    {
        uint32_t checksum = 0;
        for (size_t i = 0; i != size / 4; ++i)
        {
            checksum +=  *(data+i);
        }
        return checksum;
    }


int main()
{
    const char* data = "ABCDEFGH";
    std::cout << compute_checksum_memcpy(data, 8) << " OK\n";
    std::cout << compute_checksum_address_recast(data, 8) << " OK\n";
    std::cout << compute_checksum_union(data, 8) << " OK\n";
    std::cout << compute_checksum_deref(data, 8) << " Fail\n";
    std::cout << compute_checksum_cast(data, 8) << " Fail\n";
}

나는이 스레드가 잠시 동안 활동하지 않았다는 것을 알고 있지만, 이런 종류의 간단한 일반적인 캐스팅 루틴을 게시 할 것이라고 생각했습니다.

// safely cast between types without breaking strict aliasing rules
template<typename ReturnType, typename OriginalType>
ReturnType Cast( OriginalType Variable )
{
    union
    {
        OriginalType    In;
        ReturnType      Out;
    };

    In = Variable;
    return Out;
}

// example usage
int i = 0x3f800000;
float f = Cast<float>( i );

누군가를 돕기를 바랍니다!

이것은 언제 사용될시기의 케이스 북 예처럼 보입니다. reinterpret_cast, 다른 것은 공식적인 용도로 언어 구조를 사용하여 얻는 명시 적으로 동일한 효과를 줄 것입니다.

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