Program Tip

매달린 포인터를 비교하는 것이 합법적입니까?

programtip 2020. 11. 2. 08:24
반응형

매달린 포인터를 비교하는 것이 합법적입니까?


매달린 포인터를 비교하는 것이 합법적입니까?

int *p, *q;
{
    int a;
    p = &a;
}
{
    int b;
    q = &b;
}
std::cout << (p == q) << '\n';

p둘 다 q이미 사라진 개체를 가리키는 방법에 유의하십시오 . 합법적입니까?


소개 : 첫 번째 문제는 가치 p를 전혀 사용하는 것이 합법적인지 여부 입니다.

a소멸 유효하지 않은 포인터 값을p 획득 합니다 . 에서 견적 N4430 (N4430의 상태에 대한 설명은 아래의 "주"참조)

스토리지 영역의 지속 기간이 끝나면 할당 해제 된 스토리지의 일부 주소를 나타내는 모든 포인터의 값이 잘못된 포인터 값이됩니다 .

유효하지 않은 포인터 값이 사용되는 경우의 동작은 N4430의 동일한 섹션에서도 다룹니다 (그리고 거의 동일한 텍스트가 C ++ 14 [basic.stc.dynamic.deallocation] / 4에 나타납니다).

유효하지 않은 포인터 값을 통한 간접 지정 및 유효하지 않은 포인터 값을 할당 해제 함수에 전달하면 동작이 정의되지 않습니다. 잘못된 포인터 값을 다른 용도로 사용하면 구현 정의 동작이 있습니다.

[ 각주 : 일부 구현에서는 잘못된 포인터 값을 복사하면 시스템 생성 런타임 오류가 발생한다고 정의 할 수 있습니다. — 각주 끝]

따라서 여기에서 어떤 일이 발생해야하는지 (C ++ 14 이후) 구현 문서를 참조해야합니다.

위 따옴표에서 사용 하는 용어 는 C ++ 14 [conv.lval / 2] 에서와 같이 lvalue에서 rvalue로 변환해야 함을 의미 합니다 .

lvalue에서 rvalue 로의 변환이 e 표현식에 적용되고 [...] glvalue가 참조하는 객체에 잘못된 포인터 값이 포함 된 경우 동작은 구현에 따라 정의됩니다.


히스토리 : C ++ 11에서 이것은 구현 정의가 아니라 정의되지 않았다고 말했습니다 . DR1438에 의해 변경되었습니다 . 전체 인용문은이 게시물의 편집 내역을 참조하십시오.


에 응용 프로그램은 p == q: 우리는 평가의 결과 것을 C ++ 14 + N4430에 수락 랬 pq구현이 하드웨어 트랩이 발생하는 것을 정의하지 않는 구현 정의, 그리고; [expr.eq] / 2 내용 :

두 포인터가 모두 null이거나, 둘 다 동일한 함수를 가리 키거나, 둘 다 동일한 주소 (3.9.2)를 나타내는 경우 동일하게 비교되고, 그렇지 않으면 동일하지 않은 것으로 비교됩니다.

구현에 따라 어떤 값이 언제 얻어 p지고 q평가 되는지 정의 되었기 때문에 여기서 어떤 일이 일어날 지 확신 할 수 없습니다. 그러나 구현 정의되거나 지정되지 않아야합니다.

이 경우 g ++는 지정되지 않은 동작을 보이는 것으로 보입니다. 에 따라 -O스위치 나는 두 말할 가질 수 있었다 1또는 0재 사용 여부 같은 메모리 주소에 해당하는 ba파괴되었다.


N4430에 대한 참고 사항 : 이것은 C ++ 14에 제안 된 결함 해결 방법이며 아직 승인되지 않았습니다. 개체 수명, 유효하지 않은 포인터, 하위 개체, 공용체 및 배열 경계 액세스를 둘러싼 많은 문구를 정리합니다.

C ++ 14 텍스트에서는 [basic.stc.dynamic.deallocation] / 4 및 이후 단락에서 사용시 잘못된 포인터 값 이 발생 하는 것으로 정의되어 delete있습니다. 그러나 동일한 원칙이 정적 또는 자동 저장에 적용되는지 여부는 명확하지 않습니다.

[basic.compound] / 3에는 "유효한 포인터"라는 정의가 있지만 현명하게 사용하기에는 너무 모호합니다. [basic.life] / 5 (각주)는 동일한 텍스트를 참조하여 개체에 대한 포인터의 동작을 정의합니다. 모든 유형의 스토리지에 적용하기위한 것임을 시사하는 정적 스토리지 기간.

N4430에서는 텍스트가 해당 섹션에서 한 수준 위로 이동되어 모든 저장 기간에 명확하게 적용됩니다. 첨부 된 메모가 있습니다.

초안 참고 : 동적 저장 기간뿐만 아니라 종료 할 수있는 모든 저장 기간에 적용되어야합니다. 스레드 또는 세그먼트 스택을 지원하는 구현에서 스레드 및 자동 저장소는 동적 저장소와 동일한 방식으로 작동 할 수 있습니다.


내 의견 :p 잘못된 포인터 값 얻는다는 것 외에는 표준 (N4430 이전)을 해석하는 일관된 방법이 없다고 생각 합니다. 이 동작은 우리가 이미 살펴본 것 외에 다른 섹션에서 다루지 않는 것 같습니다. 따라서이 경우 N4430 문구를 표준의 의도를 나타내는 것으로 취급하게되어 기쁩니다.



역사적으로 포인터를 rvalue로 사용하면 시스템이 해당 포인터의 일부 비트로 식별되는 일부 정보를 가져올 수있는 시스템이 있습니다. 예를 들어, 포인터가 개체에 대한 오프셋과 함께 개체 헤더의 주소를 포함 할 수있는 경우 포인터를 가져 오면 시스템이 해당 헤더에서 일부 정보를 가져올 수도 있습니다. 객체가 존재하지 않으면 헤더에서 정보를 가져 오려는 시도가 임의의 결과로 실패 할 수 있습니다.

즉, 대부분의 C 구현에서 특정 시점에 활성 상태였던 모든 포인터는 해당 특정 시간에 있었던 관계형 및 빼기 연산자와 관련하여 영원히 동일한 관계를 유지합니다. 한 경우 실제로, 대부분의 구현 예에서 char *p, 하나는 식별 목적의 일부 식별 여부를 판정 할 수 char *base; size_t size;있는지 여부를 확인하여 (size_t)(p-base) < size, 이러한 비교는 객체의 수명이 겹치는 경우에도 소급 적으로 작동합니다.

안타깝게도 표준은 코드가 후자의 보증을 요구함을 나타낼 수있는 수단을 정의하지 않으며, 특정 구현이 후자의 동작을 약속 할 수 있는지 여부를 묻고 그렇지 않은 경우 컴파일을 거부 할 수있는 표준 수단도 없습니다. . 또한 일부 초현대적 구현에서는 두 포인터에 대한 관계형 또는 빼기 연산자의 사용을 해당 포인터가 항상 동일한 라이브 객체를 식별 할 것이라는 프로그래머의 약속으로 간주하고 해당 가정에서만 관련이있는 코드를 생략합니다. 참지 않았다. 결과적으로 많은 하드웨어 플랫폼이 많은 알고리즘에 유용한 보증을 제공 할 수 있지만


포인터는 참조하는 변수의 주소를 포함합니다. 주소는 거기에 저장되었던 변수가 해제 / 파기 / 사용 불가능한 경우에도 유효합니다. 해당 주소에서 값을 사용하지 않는 한 안전합니다. 즉, * p 및 * q는 정의되지 않습니다.

분명히 결과는 구현이 정의되어 있으므로 어셈블리 코드를 파헤 치고 싶지 않은 경우이 코드 예제를 사용하여 컴파일러의 기능을 연구 할 수 있습니다.

이것이 의미있는 관행인지 여부는 완전히 다른 논의입니다.

참고 URL : https://stackoverflow.com/questions/30694069/is-it-legal-to-compare-dangling-pointers

반응형