참조 해제 유형이 실행된 포인터는 엄격한 별칭 규칙을 위반합니다.
다음 코드를 사용하여 더 큰 프로그램의 일부로 파일에서 데이터를 읽었습니다.
double data_read(FILE *stream,int code) {
char data[8];
switch(code) {
case 0x08:
return (unsigned char)fgetc(stream);
case 0x09:
return (signed char)fgetc(stream);
case 0x0b:
data[1] = fgetc(stream);
data[0] = fgetc(stream);
return *(short*)data;
case 0x0c:
for(int i=3;i>=0;i--)
data[i] = fgetc(stream);
return *(int*)data;
case 0x0d:
for(int i=3;i>=0;i--)
data[i] = fgetc(stream);
return *(float*)data;
case 0x0e:
for(int i=7;i>=0;i--)
data[i] = fgetc(stream);
return *(double*)data;
}
die("data read failed");
return 1;
}
이제 사용하라고 합니다.-O2
gcc warning을 : 그고다 같은과 gcc 경받습 다니를고.warning: dereferencing type-punned pointer will break strict-aliasing rules
구글링 저는 두 가지 직교 답을 찾았습니다.
대
- 따라서 기본적으로 int*와 float*이 있으면 동일한 메모리 위치를 가리킬 수 없습니다.코드가 이를 준수하지 않으면 컴파일러의 최적화 도구가 코드를 손상시킬 가능성이 높습니다.
결국 저는 경고를 무시하고 싶지 않습니다.무엇을 추천하시겠습니까?
[update] 장난감 예제를 실제 기능으로 대체했습니다.
는 이문는다통에해음액세발때다스생니문합기하에를 합니다.double*
:
char data[8];
...
return *(double*)data;
그러나 gcc는 당신의 프로그램이 다른 유형의 포인터를 통해 절대 변수에 접근하지 않을 것이라고 가정합니다.이러한 가정을 엄격한 앨리어싱이라고 하며 컴파일러가 다음과 같이 최적화할 수 있습니다.
가 당신의 컴일러있경우는알고를 알고 ,*(double*)
수없는칠겹과 결코 수 .data[]
것과 같은 됩니다: 코드재과같모종허작다용니됩업이류의든은주.
return *(double*)data;
for(int i=7;i>=0;i--)
data[i] = fgetc(stream);
루프가 최적화되어 다음과 같은 결과를 얻을 수 있습니다.
return *(double*)data;
그러면 데이터가 초기화되지 않습니다.이 특별한 경우 컴파일러는 포인터가 겹치는 것을 볼 수 있지만, 만약 당신이 그것을 선언했다면.char* data
그것은 벌레를 줄 수 있었습니다.
그러나 엄격한 별칭 규칙에 따르면 char*와 void*는 모든 유형을 가리킬 수 있습니다.따라서 다음으로 다시 작성할 수 있습니다.
double data;
...
*(((char*)&data) + i) = fgetc(stream);
...
return data;
엄격한 별칭 지정 경고는 이해하거나 수정하는 데 매우 중요합니다.그들은 특정 컴퓨터의 특정 운영체제의 특정 컴파일러 하나에서만 발생하고 보름달과 일년에 한 번만 발생하기 때문에 사내에서 복제가 불가능한 종류의 버그를 유발합니다.
당신이 정말로 fread를 사용하고 싶어하는 것처럼 보입니다.
int data;
fread(&data, sizeof(data), 1, stream);
즉, 만약 당신이 문자를 읽고 그것들을 int로 재해석하고 싶다면, C에서 그것을 하는 안전한 방법은 유니언을 사용하는 것입니다.
union
{
char theChars[4];
int theInt;
} myunion;
for(int i=0; i<4; i++)
myunion.theChars[i] = fgetc(stream);
return myunion.theInt;
나는 왜길 이가의 가 긴지 잘 data
당신의 원래 코드는 3입니다.당신이 4바이트를 원했을 거라고 생각합니다. 적어도 int가 3바이트인 시스템은 모릅니다.
귀하의 코드와 제 코드는 모두 휴대가 불가능합니다.
편집: 파일에서 다양한 길이의 int를 읽으려면 다음과 같은 방법을 사용하십시오.
unsigned result=0;
for(int i=0; i<4; i++)
result = (result << 8) | fgetc(stream);
(참고: 실제 프로그램에서는 EOF에 대해 fgetc()의 반환 값을 추가로 테스트할 수 있습니다.)
이는 시스템의 엔디언에 관계없이 리틀 엔디언 형식으로 파일에서 서명되지 않은 4바이트를 읽습니다.서명되지 않은 시스템이 4바이트 이상인 경우에는 거의 모든 시스템에서 작동해야 합니다.
엔디안 중립적이 되고 싶다면 포인터나 유니언을 사용하지 말고 비트 시프트를 사용하십시오.
여기서 노조를 사용하는 것은 옳은 일이 아닙니다.유니온의 비기입 구성원의 읽기는 정의되지 않았습니다. 즉, 컴파일러는 자유롭게 코드를 파괴하는 최적화를 수행할 수 있습니다(쓰기를 최적화하는 것처럼).
이 문서는 상황을 요약합니다. http://dbp-consulting.com/tutorials/StrictAliasing.html
여러가지 해결책이 있지만, 가장 휴대성이 좋고 안전한 방법은 memcpy()를 사용하는 것입니다. (함수 호출이 최적화될 수 있기 때문에 보이는 것만큼 비효율적이지 않습니다.)예를 들어, 다음과 같이 바꿉니다.
return *(short*)data;
사용:
short temp;
memcpy(&temp, data, sizeof(temp));
return temp;
기본적으로 당신은 문제를 찾고 있는 남자로서 gcc의 메시지를 읽을 수 있습니다, 제가 당신에게 경고하지 않았다고 말하지 마세요.
을 3바이트 문자 배열로 int
내가 본 것 중 최악의 것 중 하나야. 당신의 보통당의.int
최소 4바이트입니다. 네는 ( 더 경우에는).int
더 넓음) 랜덤 데이터를 얻을 수 있습니다.그리고 이 모든 것들을double
.
아무 것도 하지 마세요.gcc가 경고하는 aliasing 문제는 당신이 하고 있는 것에 비해 무죄입니다.
C 표준의 저자들은 이론적으로 가능하지만 글로벌 변수가 관련이 없어 보이는 포인터를 사용하여 값에 액세스할 가능성이 없는 상황에서 컴파일러 작성자가 효율적인 코드를 생성할 수 있도록 했습니다.이 아이디어는 단일 표현식에서 포인터를 캐스팅하고 참조를 해제함으로써 타이프 펀칭을 금지하는 것이 아니라 다음과 같은 것이 주어지면 그렇게 말하는 것이었습니다.
int x;
int foo(double *d)
{
x++;
*d=1234;
return x;
}
컴파일러는 *d에 대한 쓰기가 x에 영향을 미치지 않는다고 가정할 수 있습니다.이 기준서의 작성자들은 알 수 없는 출처로부터 포인터를 받은 위와 같은 함수가 해당 유형이 완벽하게 일치할 필요 없이 겉보기에 관련이 없어 보이는 전역에 별칭을 붙일 수 있다고 가정해야 하는 상황을 나열하려고 했습니다.불행하게도, 이론적 근거는 이 기준서의 저자들이 컴파일러가 그렇지 않으면 상황이 별칭이 될 수 있다고 믿을 이유가 없는 경우에 최소한의 적합성에 대한 표준을 설명하려고 의도했다는 것을 강하게 시사하지만,규칙은 컴파일러가 앨리어싱을 인식하도록 요구하지 않으며 gcc의 저자들은 실제로 유용한 코드를 생성하는 것보다 표준의 잘 작성되지 않은 언어를 준수하면서 할 수 있는 가장 작은 프로그램을 생성하기로 결정했습니다.그리고 명백한 경우에 앨리어싱을 인식하는 대신(에일리어스처럼 보이지 않는 것들이 에리어싱을 할 것이라고 가정할 수 있지만) 프로그래머들이 사용하도록 요구하는 것이 더 나을 것입니다.memcpy
따라서 컴파일러가 출처를 알 수 없는 포인터가 거의 모든 것에 대해 별칭을 지정할 수 있으므로 최적화를 방해할 수 있습니다.
보아하니 이 표준은 직접 캐스팅을 시도할 때 sogcc 불만 사항의 size(char*)와 sogcc 불만 사항의 size(int*)가 다를 수 있도록 허용합니다.void*는 모든 것이 void*로 앞뒤로 변환될 수 있다는 점에서 약간 특별합니다.실제로 포인터가 모든 유형에 대해 항상 같지는 않지만 귀찮더라도 gcc가 경고를 보내는 것이 맞는 아키텍처/컴파일러는 잘 모릅니다.
안전한 방법은
int i, *p = &i;
char *q = (char*)&p[0];
또는
char *q = (char*)(void*)p;
또한 이 기능을 사용하여 다음과 같은 이점을 얻을 수 있습니다.
char *q = reinterpret_cast<char*>(p);
언급URL : https://stackoverflow.com/questions/3246228/dereferencing-type-punned-pointer-will-break-strict-aliasing-rules
'source' 카테고리의 다른 글
종료하지 않고 MariaDB 데이터베이스를 남깁니다. (0) | 2023.07.05 |
---|---|
Windows 인증을 사용하여 SQL 연금술을 통해 SQL Server에 연결하는 방법은 무엇입니까? (0) | 2023.07.05 |
mongoose에서 스키마를 전달하지 않고 컬렉션 쿼리 (0) | 2023.07.05 |
"참조" 권한이 필요한 이유는 무엇입니까? (0) | 2023.07.05 |
한 번에 여러 개의 물건을 한 줄에 하나씩 인쇄하려면 어떻게 해야 합니까? (0) | 2023.07.05 |