[c++] Visual Studio는 삭제 된 포인터로 무엇을하며 그 이유는 무엇입니까?

필자가 읽은 C ++ 책은 포인터를 delete연산자를 사용하여 삭제하면 가리키는 위치의 메모리가 “해제”되어 덮어 쓸 수 있다고 말합니다. 또한 포인터가 재 할당되거나로 설정 될 때까지 동일한 위치를 계속 가리킬 것임을 나타냅니다.NULL .

그러나 Visual Studio 2012에서는; 이것은 사실이 아닌 것 같습니다!

예:

#include <iostream>

using namespace std;

int main()
{
    int* ptr = new int;
    cout << "ptr = " << ptr << endl;
    delete ptr;
    cout << "ptr = " << ptr << endl;

    system("pause");

    return 0;
}

이 프로그램을 컴파일하고 실행하면 다음과 같은 결과가 나타납니다.

ptr = 0050BC10
ptr = 00008123
Press any key to continue....

delete가 호출 될 때 포인터가 가리키는 주소는 분명히 변경됩니다!

왜 이런 일이 발생합니까? 이것은 Visual Studio와 관련이 있습니까?

그리고 delete가 어쨌든 가리키는 주소를 변경할 수 있다면 왜 NULL임의의 주소 대신 포인터를 자동으로 설정하지 않습니까?



답변

저장된 주소 ptr가 항상 덮어 쓰여지는 것을 알았 습니다.00008123

이것은 이상하게 보였으므로 약간의 파고를했고 “C ++ 객체를 삭제할 때 자동 포인터 삭제”에 대해 설명하는 섹션이 포함 된 이 Microsoft 블로그 게시물 을 찾았 습니다 .

… NULL 검사는 공통 코드 구성을 의미합니다. 기존의 NULL 검사는 살균 값으로 NULL을 사용하는 것과 결합하여 근본 원인을 해결해야하는 실제 메모리 안전 문제를 숨길 수 있습니다.

이러한 이유로 우리는 살균 값으로 0x8123을 선택했습니다. 운영 체제 관점에서 이것은 제로 주소 (NULL)와 동일한 메모리 페이지에 있지만 0x8123에서의 액세스 위반은보다 세심한주의가 필요하므로 개발자에게 더 잘 드러날 것입니다. .

Visual Studio가 포인터를 삭제 한 후 포인터로 수행하는 작업을 설명 할뿐만 아니라 NULL자동으로 설정하지 않기로 선택한 이유에 대한 답변도 제공합니다 .


이 “기능”은 “SDL 검사”설정의 일부로 활성화됩니다. 활성화 / 비활성화하려면 PROJECT-> 속성-> 구성 속성-> C / C ++-> 일반-> SDL 검사로 이동하십시오.

이를 확인하려면 다음을 수행하십시오.

이 설정을 변경하고 동일한 코드를 다시 실행하면 다음과 같은 출력이 생성됩니다.

ptr = 007CBC10
ptr = 007CBC10

“기능”은 살균 삭제를 호출, 같은 위치에 두 개의 포인터를 가지고 있기 때문에 경우에 따옴표에 하나 그들 중입니다. 다른 하나는 잘못된 위치를 가리키고 있습니다 …


최신 정보:

5 년 이상의 C ++ 프로그래밍 경험을 통해이 모든 문제가 기본적으로 문제가된다는 것을 알게되었습니다. C ++ 프로그래머이고 스마트 포인터를 사용하지 않고 원시 포인터를 사용 new하고 delete관리하는 경우 (이 전체 문제를 우회) C 프로그래머가되기 위해 경력 경로를 변경하는 것이 좋습니다. 😉


답변

/sdl컴파일 옵션의 부작용이 나타납니다 . VS2015 프로젝트에 대해 기본적으로 켜져 있으며 / gs에서 제공하는 것 이외의 추가 보안 검사가 가능합니다. 프로젝트> 속성> C / C ++> 일반> SDL 검사 설정을 사용하여 변경하십시오.

MSDN 기사 에서 인용 :

  • 제한된 포인터 살균을 수행합니다. 역 참조를 포함하지 않는 표현식과 사용자 정의 소멸자가없는 유형에서는 포인터 호출이 삭제 호출 후 유효하지 않은 주소로 설정됩니다. 이로 인해 오래된 포인터 참조가 재사용되지 않습니다.

삭제 된 포인터를 NULL로 설정하는 것은 MSVC를 사용할 때 좋지 않은 방법입니다. 디버그 힙과이 / sdl 옵션 모두에서 얻는 도움말을 무시하므로 더 이상 프로그램에서 유효하지 않은 비어있는 / 삭제 호출을 감지 할 수 없습니다.


답변

또한 포인터가 재 할당되거나 NULL로 설정 될 때까지 포인터가 계속 동일한 위치를 가리킬 것입니다.

그것은 확실히 잘못된 정보입니다.

delete가 호출 될 때 포인터가 가리키는 주소는 분명히 변경됩니다!

왜 이런 일이 발생합니까? 이것은 Visual Studio와 관련이 있습니까?

이것은 분명히 언어 사양 내에 있습니다. ptr에 호출 한 후 유효하지 않습니다 delete. d ptr이후에 사용하면 delete정의되지 않은 동작이 발생합니다. 하지마 런타임 환경은에 ptr대한 호출 후 원하는 것을 자유롭게 수행 할 수 delete있습니다.

그리고 delete가 어쨌든 가리키는 주소를 변경할 수 있다면 왜 임의의 주소 대신 포인터를 자동으로 NULL로 설정하지 않습니까?

포인터 값을 이전 값으로 변경하는 것은 언어 사양 내에 있습니다. NULL로 변경하는 한, 그것은 나쁠 것입니다. 포인터의 값이 NULL로 설정되면 프로그램은보다 정상적인 방식으로 작동합니다. 그러나 문제가 숨겨집니다. 프로그램이 다른 최적화 설정으로 컴파일되거나 다른 환경으로 이식되면 문제가 가장 부적절한 순간에 나타날 수 있습니다.


답변

delete ptr;
cout << "ptr = " << ptr << endl;

일반도에서 읽기 (위에서처럼, 참고 :이 역 참조 다릅니다) 유효하지 않은 포인터의 값 (당신이 때 포인터가 예를 들어 무효가 delete구현 정의 된 동작입니다 그것은). 이것은 CWG # 1438 에서 소개되었습니다 . 여기도 참조 하십시오 .

유효하지 않은 포인터의 값을 읽는 것은 정의되지 않은 행동이기 때문에, 위의 내용은 정의되지 않은 행동이므로 모든 일이 발생할 수 있습니다.


답변

나는 당신이 일종의 디버그 모드를 실행 중이고 VS가 포인터를 알려진 위치로 다시 지정하려고 시도하여 역 참조를 시도하면 추적 및보고 될 수 있다고 생각합니다. 릴리스 모드에서 동일한 프로그램을 컴파일 / 실행하십시오.

delete효율성을 위해 그리고 안전에 대한 잘못된 생각을 피하기 위해 일반적으로 포인터는 내부 에서 변경되지 않습니다 . 삭제 포인터가이 위치를 가리키는 몇 개 중 하나 일 가능성이 있기 때문에 대부분의 복잡한 시나리오에서 삭제 포인터를 사전 정의 된 값으로 설정하면 좋지 않습니다.

사실, 내가 그것에 대해 더 많이 생각할수록 평소와 같이 그렇게 할 때 VS가 잘못되었다는 것을 알게됩니다. 포인터가 const라면 어떨까요? 여전히 변경 될까요?


답변

포인터를 삭제 한 후에 가리키는 메모리는 여전히 유효 할 수 있습니다. 이 오류를 나타 내기 위해 포인터 값은 명백한 값으로 설정됩니다. 이것은 실제로 디버깅 프로세스에 도움이됩니다. 값이로 설정된 NULL경우 프로그램 흐름에서 잠재적 인 버그로 표시되지 않을 수 있습니다. 따라서 나중에 테스트 할 때 버그를 숨길 수 있습니다 NULL.

또 다른 요점은 일부 런타임 최적화 프로그램이 해당 값을 확인하고 결과를 변경할 수 있다는 것입니다.

이전에는 MS가 값을로 설정했습니다 0xcfffffff.


답변