왜 이 주장된 역참조 유형이 실행된 포인터 경고가 컴파일러에 특정적입니까?
나는 스택 오버플로 RE에 대한 다양한 게시물을 읽었다: 디레퍼레이팅 타입 펀치 포인터 오류.제가 이해하기로는 이 오류는 본질적으로 컴파일러가 다른 유형의 포인터를 통해 객체에 액세스할 위험을 경고하는 것입니다(그러나 예외가 발생한 것으로 보입니다).char*할 수 있고 경고입니다.).
아래 코드에 대한 질문입니다. 포인터의 주소를 에 캐스팅하면 이 경고(를 통해 오류로 승격됨)가 적용되는 이유는 무엇입니까?
게다가, 이 코드는 여러 대상 아키텍처에 대해 컴파일되며, 그 중 하나만 경고/오류를 생성합니다. 이것이 합법적으로 컴파일러 버전별 결함임을 의미할 수 있습니까?
// main.c
#include <stdlib.h>
typedef struct Foo
{
int i;
} Foo;
void freeFunc( void** obj )
{
if ( obj && * obj )
{
free( *obj );
*obj = NULL;
}
}
int main( int argc, char* argv[] )
{
Foo* f = calloc( 1, sizeof( Foo ) );
freeFunc( (void**)(&f) );
return 0;
}
와 같은 제 가 맞다면,void**여전히 포인터일 뿐이므로, 이것은 안전한 캐스팅이 될 것입니다.
컴파일러별 경고/오류를 완화할 수 있는 lvalue를 사용하지 않는 해결 방법이 있습니까?즉, 저는 그것과 왜 이것이 문제를 해결할 수 있는지 이해하지만, 저는 이 접근법을 이용하고 싶기 때문에 피하고 싶습니다.freeFunc() 의도된 인수를 제외한 NULL:
void* tmp = f;
freeFunc( &tmp );
f = NULL;
문제 컴파일러(하나):
user@8d63f499ed92:/build$ /usr/local/crosstool/x86-fc3/bin/i686-fc3-linux-gnu-gcc --version && /usr/local/crosstool/x86-fc3/bin/i686-fc3-linux-gnu-gcc -Wall -O2 -Werror ./main.c
i686-fc3-linux-gnu-gcc (GCC) 3.4.5
Copyright (C) 2004 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
./main.c: In function `main':
./main.c:21: warning: dereferencing type-punned pointer will break strict-aliasing rules
user@8d63f499ed92:/build$
불만이 없는 컴파일러(여러 컴파일러 중 하나):
user@8d63f499ed92:/build$ /usr/local/crosstool/x86-rh73/bin/i686-rh73-linux-gnu-gcc --version && /usr/local/crosstool/x86-rh73/bin/i686-rh73-linux-gnu-gcc -Wall -O2 -Werror ./main.c
i686-rh73-linux-gnu-gcc (GCC) 3.2.3
Copyright (C) 2002 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
user@8d63f499ed92:/build$
업데이트: 특히 다음을 사용하여 컴파일할 때 경고가 생성되는 것으로 나타났습니다.-O2 컴파일러 표시됨 ("문제 컴파일러"로 표시됨)
의 값 값void**유형의 개체에 대한 포인터입니다.void*이 객유인 Foo*는 형식의 .void*.
유형 값 사이에 암묵적인 변환이 있습니다.Foo*그리고.void*이 변환은 값의 표현을 변경할 수 있습니다.마찬가지로, 당신은 쓸 수 있습니다.int n = 3; double x = n;그리고 이것은 설정의 잘 정의된 행동을 가지고 있습니다.x의 3.0,그렇지만double *p = (double*)&n;되지 않은는 설정하지 ).p에3.0모든 공통 아키텍처에서).
객체에 대한 다른 유형의 포인터가 다른 표현을 갖는 아키텍처는 현재 드물지만 C 표준에 의해 허용됩니다.메모리에 있는 단어의 주소인 단어 포인터와 이 단어의 바이트 오프셋과 함께 단어의 주소인 바이트 포인터가 있는 (희귀한) 오래된 기계가 있습니다.Foo*와 단포터될것이고가인어,,void*이러한 아키텍처에서는 바이트 포인터가 됩니다.개체의 주소뿐만 아니라 유형, 크기 및 액세스 제어 목록에 대한 정보를 포함하는 지방 포인터를 가진 (희귀한) 기계가 있습니다. 확실한 유형에 대한 포인터는 다음과 다른 표현을 가질 수 있습니다.void*런타임에 추가 형식 정보가 필요합니다.
이러한 기계는 드물지만 C 표준에 의해 허용됩니다.그리고 일부 C 컴파일러는 코드를 최적화하기 위해 유형으로 펀칭된 포인터를 구별되는 것으로 처리할 수 있는 권한을 활용합니다.포인터 앨리어싱의 위험은 컴파일러의 코드 최적화 기능에 큰 제한이 되므로 컴파일러는 이러한 권한을 활용하는 경향이 있습니다.
컴파일러는 여러분이 무언가 잘못하고 있다고 말하거나, 여러분이 원하지 않았던 것을 조용히 하거나, 여러분이 원하는 것을 조용히 할 수 있습니다.정의되지 않은 동작은 다음 중 하나를 허용합니다.
만들 수 있습니다.freefunc매크로:
#define FREE_SINGLE_REFERENCE(p) (free(p), (p) = NULL)
이는 매크로의 일반적인 한계와 함께 발생합니다. 유형 안전성의 부족,p두 번 평가됩니다.이것은 다음과 같은 경우에 달그락달그락 포인터를 남기지 않는 안전성만 제공합니다.p해제된 개체에 대한 단일 포인터입니다.
A void *불완전한 유형을 참조하기 때문에 부분적으로 C 표준에 의해 특별히 처리됩니다.이 치료는 다음으로 확장되지 않습니다.void **그것이 완전한 유형을 가리키기 때문에, 특히.void *.
엄격한 별칭 규칙에 따르면 한 유형의 포인터를 다른 유형의 포인터로 변환할 수 없으며 한 유형의 바이트를 다른 유형으로 재해석해야 하므로 포인터의 참조를 해제할 수 없습니다.유일한 예외는 객체의 표현을 읽을 수 있는 문자 유형으로 변환하는 경우입니다.
함수 대신 함수와 같은 매크로를 사용하면 이러한 제한을 피할 수 있습니다.
#define freeFunc(obj) (free(obj), (obj) = NULL)
다음과 같이 부를 수 있습니다.
freeFunc(f);
그러나 위의 매크로가 평가하기 때문에 제한이 있습니다.obj 번. GCC를 ". GCC", "GCC", "GCC"를 에는 이를 피할 수 .typeof키워드 및 문 식:
#define freeFunc(obj) ({ typeof (&(obj)) ptr = &(obj); free(*ptr); *ptr = NULL; })
유형 펀칭 포인터를 참조 해제하는 것은 UB이며 무슨 일이 일어날지 기대할 수 없습니다.
컴파일러마다 경고가 다르므로 동일한 컴파일러의 버전이 다를 경우 다른 컴파일러로 간주할 수 있습니다.이것은 아키텍처에 의존하는 것보다 여러분이 보는 분산에 대한 더 나은 설명인 것 같습니다.
이 경우에 type punning이 나쁜 이유를 이해하는 데 도움이 될 수 있는 사례는 당신의 기능이 다음을 위한 아키텍처에서 작동하지 않는다는 것입니다.sizeof(Foo*) != sizeof(void*)저는 이것이 사실인 현재의 어떤 것에 대해서도 모르지만 그것은 표준에 의해 승인되었습니다.
해결 방법은 함수 대신 매크로를 사용하는 것입니다.
:freepointers를 할 수 . 는 null pointers를 사용합니다.
이 코드는 C 표준에 따라 유효하지 않기 때문에 경우에 따라 작동할 수 있지만 반드시 휴대할 수 있는 것은 아닙니다.
다른 포인터 유형으로 캐스팅된 포인터를 통해 값에 액세스하기 위한 "엄격한 별칭 규칙"은 6.5항 7에 나와 있습니다.
객체는 다음 유형 중 하나를 가진 값 표현식을 통해서만 저장된 값에 액세스할 수 있습니다.
개체의 유효 유형과 호환되는 유형,
개체의 유효한 형식과 호환되는 형식의 정규 버전
객체의 유효 유형에 해당하는 서명 또는 미서명 유형인 유형,
객체의 유효 형식의 한정된 버전에 해당하는 서명 또는 서명되지 않은 형식인 형식,
조합원들 사이에서 전술한 유형 중 하나를 포함하는 집합체 또는 조합 유형(재귀적으로 하위 집합체 또는 포함된 조합의 구성원 포함), 또는
인물 활자
의 신의에서.*obj = NULL; 유형은 문, 개에유유형있습니다입니다.Foo* 식 하만lvalue ▁by로 .*obj활자가 void*.
6.7.5.1 제2항에서, 우리는 다음과 같습니다.
호환 가능한 두 포인터 유형의 경우, 두 포인터 모두 동일한 자격을 갖추어야 하며 두 포인터 모두 호환 가능한 유형에 대한 포인터여야 합니다.
그렇게void*그리고.Foo*호환되지 않는 유형이거나 한정자가 추가된 호환 유형이 아니며 엄격한 별칭 규칙의 다른 옵션에도 적합하지 않습니다.
코드가 유효하지 않은 기술적 이유는 아니지만 섹션 6.2.5 26항에도 관련이 있습니다.
에 대한
void문자 형식에 대한 포인터와 동일한 표현 및 정렬 요구 사항을 가져야 합니다.마찬가지로, 호환 가능한 형식의 정규화된 버전 또는 비정규화된 버전에 대한 포인터는 동일한 표현 및 정렬 요구사항을 가져야 합니다.구조물 유형에 대한 모든 포인터는 서로 동일한 표현 및 정렬 요구사항을 가져야 합니다.조합 유형에 대한 모든 포인터는 서로 동일한 표현 및 정렬 요구사항을 가져야 합니다.다른 유형에 대한 포인터가 동일한 표현 또는 정렬 요구 사항을 가질 필요는 없습니다.
경고의 차이에 대해 말하자면, 이것은 표준에서 진단 메시지를 요구하는 경우가 아니므로 컴파일러 또는 해당 버전이 잠재적인 문제를 인지하고 유용한 방법으로 지적하는 데 얼마나 능숙한지에 대한 문제일 뿐입니다.최적화 설정으로 인해 차이가 발생할 수 있습니다.이는 종종 프로그램의 다양한 부분이 실제로 어떻게 결합되는지에 대한 더 많은 정보가 내부적으로 생성되기 때문에 추가 정보를 경고 검사에도 사용할 수 있습니다.
다른 대답들이 말한 것 외에도, 이것은 C의 고전적인 안티패턴이며, 불에 태워져야 하는 패턴입니다.다음에 표시됩니다.
- 경고를 발견한 것과 같은 프리 앤 널 아웃 기능입니다.
- 반환의
void *(이 문제는 유형 펀닝 대신 값 변환을 포함하기 때문에 발생하지 않음) 대신 오류 플래그를 반환하고 포인터에서 포인터로 결과를 저장합니다.
(의 또 로, (1) 또다예로른서, ffmpeg/libavcodec의 경우 오랫동안 악명 .av_free. 것 잘결국 매크로나 다른 트릭으로 고쳐졌다고 생각합니다만, 잘 모르겠습니다.
(2)의 경우, 둘 다 및posix_memalign예를 들어 보겠습니다.
어느 경우에도 인터페이스는 본질적으로 잘못된 사용을 요구하지 않지만 강력하게 권장하고 유형의 임시 개체를 추가해야 올바른 사용을 허용합니다.void *이는 자유-앤-아웃 기능의 목적을 무시하고 할당을 어색하게 만듭니다.
C는 모든 포인터에 대해 동일한 표현을 사용하는 기계를 위해 설계되었지만, 이 표준의 저자들은 다른 유형의 객체에 대한 포인터에 대해 다른 표현을 사용하는 기계에서 언어를 사용할 수 있게 하고 싶었습니다.따라서 다른 종류의 포인터에 대해 다른 포인터 표현을 사용하는 기계가 "모든 종류의 포인터에 대한 포인터" 유형을 지원하도록 요구하지 않았습니다.
표준이 작성되기 전에 모든 포인터 유형에 대해 동일한 표현을 사용한 플랫폼을 위한 구현은 만장일치로 다음을 허용했습니다.void**적어도 적절한 주조와 함께 "모든 포인터에 대한 포인터"로 사용해야 합니다.이 표준의 작성자들은 이 표준이 이 표준을 지원하는 플랫폼에서 유용할 것이라는 것을 거의 확실하게 인식했지만, 이 표준이 보편적으로 지원될 수 없었기 때문에 이 표준을 의무화하는 것을 거부했습니다.대신에, 그들은 품질 구현이 타당한 경우에 이론적 근거가 설명하는 "인기 확장"과 같은 구성을 처리할 것이라고 예상했습니다.
언급URL : https://stackoverflow.com/questions/58809360/why-is-this-claimed-dereferencing-type-punned-pointer-warning-compiler-specific
'programing' 카테고리의 다른 글
| 보안 웹 소켓 서버(tomcat)를 통해 각 소켓(HTTPS)이 있는 스프링 웹 소켓(WSS) (0) | 2023.08.10 |
|---|---|
| 가상 환경을 생성해야 하는 위치 (0) | 2023.08.10 |
| C++에서 문자열과 char[] 유형의 차이 (0) | 2023.08.05 |
| CSV 데이터를 처리할 때 데이터의 첫 번째 줄을 무시하는 방법은 무엇입니까? (0) | 2023.08.05 |
| Spring Boot MongoDB 연결 문제 (0) | 2023.08.05 |