IT이야기

"타입 펀닝 포인터를 역참조하면 엄격한 앨리어싱 규칙이 깨집니다" 경고

cyworld 2021. 9. 21. 20:01
반응형

"타입 펀닝 포인터를 역참조하면 엄격한 앨리어싱 규칙이 깨집니다" 경고


enum*을 int*로 변환하는 코드를 사용합니다. 이 같은:

enum foo { ... }
...
foo foobar;
int *pi = reinterpret_cast<int*>(&foobar);

코드(g++ 4.1.2)를 컴파일할 때 다음 경고 메시지가 나타납니다.

dereferencing type-punned pointer will break strict-aliasing rules

이 메시지를 봤더니 엄격한 앨리어싱 최적화가 켜져 있을 때만 발생한다는 것을 알았습니다. 다음과 같은 질문이 있습니다.

  • 이 경고와 함께 코드를 남겨두면 잠재적으로 잘못된 코드가 생성됩니까?
  • 이 문제를 해결할 방법이 있습니까?
  • 그렇지 않은 경우 소스 파일 내부에서 엄격한 앨리어싱을 해제할 수 있습니까(모든 소스 파일에 대해 해제하고 싶지 않고 이 소스 파일에 대해 별도의 Makefile 규칙을 만들고 싶지 않기 때문에 )?

예, 실제로 이러한 종류의 앨리어싱이 필요합니다.


순서대로:

  • 예. GCC는 포인터가 별칭을 만들 수 없다고 가정합니다. 예를 들어, 하나를 통해 할당하고 다른 하나에서 읽는 경우 GCC는 최적화로 읽기 및 쓰기를 재정렬할 수 있습니다. 프로덕션 코드에서 이러한 일이 발생하는 것을 보았고 디버그하는 것이 즐겁지 않습니다.

  • 여러 개의. 유니온을 사용하여 재해석해야 하는 메모리를 나타낼 수 있습니다. 당신은 사용할 수 있습니다 reinterpret_cast. char *메모리를 재해석하는 지점에서 캐스트 char *할 수 있습니다. 이는 무엇이든 앨리어싱할 수 있는 것으로 정의됩니다. 가 있는 유형을 사용할 수 있습니다 __attribute__((__may_alias__)). -fno-strict-aliasing을 사용하여 전역적으로 앨리어싱 가정을 끌 수 있습니다.

  • __attribute__((__may_alias__)) 사용된 유형은 코드의 특정 섹션에 대한 가정을 비활성화하는 데 가장 가깝습니다.

특정 예의 경우 열거형의 크기가 잘못 정의되어 있습니다. GCC는 일반적으로 이를 나타내는 데 사용할 수 있는 가장 작은 정수 크기를 사용하므로 열거형에 대한 포인터를 정수로 재해석하면 결과 정수에 초기화되지 않은 데이터 바이트가 남을 수 있습니다. 그러지 마세요. 적절하게 큰 정수 유형으로 캐스트하지 않는 이유는 무엇입니까?


근데 왜 이러는거야? sizeof(foo) != sizeof(int)이면 중단됩니다. 열거형이 정수와 같다고 해서 하나로 저장되는 것은 아닙니다.

예, "잠재적으로" 잘못된 코드를 생성할 수 있습니다.


다음 코드를 사용하여 데이터를 캐스팅할 수 있습니다.

template<typename T, typename F>
struct alias_cast_t
{
    union
    {
        F raw;
        T data;
    };
};

template<typename T, typename F>
T alias_cast(F raw_data)
{
    alias_cast_t<T, F> ac;
    ac.raw = raw_data;
    return ac.data;
}

사용 예:

unsigned int data = alias_cast<unsigned int>(raw_ptr);

이 답변 을 살펴 보셨습니까 ?

The strict aliasing rule makes this setup illegal, two unrelated types can't point to the same memory. Only char* has this privilege. Unfortunately you can still code this way, maybe get some warnings, but have it compile fine.


Strict aliasing is a compiler option, so you need to turn it off from the makefile.

And yes, it can generate incorrect code. The compiler will effectively assume that foobar and pi aren't bound together, and will assume that *pi won't change if foobar changed.

As already mentioned, use static_cast instead (and no pointers).

ReferenceURL : https://stackoverflow.com/questions/4163126/dereferencing-type-punned-pointer-will-break-strict-aliasing-rules-warning

반응형