디버그 컴파일 실행 파일:NULL에 대한 유효하지 않은 쓰기를 정상적으로 중단하지 않는 이유는 무엇입니까?

StackOverflow https://stackoverflow.com/questions/550106

문제

C/C++에 대해 내가 이해하지 못하는 점은 다음과 같습니다.

예, 모두가 이를 사용하여 엄청나게 빠른 실행 파일을 얻으므로 최적화를 활성화한 상태로 컴파일합니다.

그러나 디버그 정보가 켜진 상태에서 컴파일하는 경우에는 속도에 신경 쓰지 않습니다.그렇다면 해당 컴파일 모드에 더 많은 정보를 포함하는 것은 어떨까요? 예를 들어 일부 세그먼트 오류가 발생하기 전에 감지합니까?효과적으로 assert(ptr != NULL) 포인터에 접근할 때마다 ptr.컴파일러는 왜 그렇게 할 수 없습니까?다시 말하지만, 기본적으로 꺼져 있어야 하지만 그런 가능성도 있을 것이라고 생각합니다.

편집하다: 어떤 사람들은 내가 제안한 탐지가 말이 되지 않거나 보고된 내용과 전혀 관련이 없다고 말했습니다. segmentation fault 이미 하고 있지는 않을 것입니다.하지만 내가 염두에 두고 있는 것은 문제가 되는 코드의 파일 이름과 줄 번호를 인쇄하는 좀 더 우아하고 유익한 중단입니다. assert() 할것이다.

도움이 되었습니까?

해결책

제안에는 몇 가지 주요 문제가 있습니다.

컴파일러가 어떤 조건을 감지하기를 원하십니까? Linux/x86에서는 정렬되지 않은 액세스가 발생할 수 있습니다 SIGBUS 그리고 스택 오버플로로 인해 발생할 수 있습니다 SIGSEGV, 그러나 두 경우 모두 기술적으로 해당 조건을 감지하고 "우아하게"실패하기 위해 응용 프로그램을 작성할 수 있습니다. NULL 포인터 검사는 감지 될 수 있지만 가장 교활한 버그는 NULL 포인터.

C 및 C ++ 프로그래밍 언어는 충분한 유연성을 제공하므로 주어진 임의의 주소가 임의의 유형의 유효한 포인터 인 경우 런타임이 100% 성공으로 결정하는 것은 불가능합니다.

이 상황을 감지 할 때 런타임 환경이 무엇을 원하십니까? 그것은 행동을 바로 잡을 수 없습니다 (마술을 믿지 않는 한). 계속 실행하거나 종료 할 수 있습니다. 그러나 잠깐만 ... 신호가 전달 될 때 이미 일어나는 일입니다! 프로그램이 종료되고 코어 덤프가 생성되며, 해당 코어 덤프는 애플리케이션 개발자가 프로그램이 충돌 할 때 프로그램의 상태를 결정하는 데 사용될 수 있습니다.

당신이 옹호하는 것은 실제로 디버거에서 응용 프로그램을 실행하고 싶은 것처럼 들립니다 (gdb) 또는 어떤 형태의 가상화를 통해 (valgrind). 이것은 이미 가능하지만, 비 개발자에게는 혜택을 제공하지 않기 때문에 기본적으로 수행하는 것은 의미가 없습니다.

의견에 응답하려면 업데이트 :

디버그 버전의 컴파일 프로세스를 수정할 이유가 없습니다. 애플리케이션의 "온화한"디버그 버전이 필요한 경우 디버거 내부에서 실행해야합니다. 투명하게이를 수행하는 스크립트로 실행 파일을 감싸는 것은 매우 쉽습니다.

다른 팁

이 경우 프로그램은 무엇을해야합니까? 그것이 버그를 사용자에게 알리면 Segfault가하는 일입니다.

계속해서 버그를 피해야한다면 어떻게 해야하는지 어떻게 알 수 있습니까?

말할 것도없이 말할 것도없이 말할 것도없이 말할 것도없이 말할 것도없이, 마술처럼 제대로 계속하는 방법을 마술처럼 알고 있다면 릴리스 빌드에 버그가 있습니다 (디버그 빌드는 버그를 식별하고 수정하는 데 도움이됩니다.


질문에 추가 된 추가 정보에 대한 응답으로 (나는 당신의 의도를 오해했다고 생각합니다) :

내가 생각하는 것은 더 우아하고 유익한 중단입니다. Assert ()처럼 파일 이름과 불쾌한 코드의 줄 번호를 인쇄합니다.

이것은 컴파일러가 할 수있는 일입니다. assert() 포인터가 부정 된 곳이라면 어디든. 이것은 디버그 빌드의 크기에 상당히 추가로 추가 될 수 있지만 아마도 많은 (또는 대부분의) 목적에서도 여전히 허용 될 것입니다. 나는 이것이 컴파일러가 제공하는 합리적인 옵션이라고 생각합니다.

컴파일러 공급 업체가 어떤 말을할지 잘 모르겠습니다 ... 아마도 요청을 게시 할 수 있습니다. VC ++ 제품의 Microsoft Connect 사이트 그리고 그들이 말하는 것을보십시오.

나는 이것이 실제로 아무 것도 하지 않거나 도움이 되지 않는다는 Michael Burr의 의견에 동의합니다.

게다가 이것은 널 포인터보다 훨씬 더 교활하고 추적하기 어려운 경향이 있는 매달린 포인터에는 여전히 작동하지 않습니다.

최소한 널 포인터를 사용하면 참조를 해제하기 전에 유효한지 확인할 수 있을 만큼 간단합니다.

원본 포스터에서는 앱이 디버거에서 중지되기를 원하는 것 같습니다.모든 스택 변수와 스택에 액세스할 수 있으므로 프로그램이 이 상태에 있는 이유를 알아낼 수 있습니다.

C/C++로 개발하는 경우 디버깅 메모리 관리자를 사용하면 엄청난 시간을 절약할 수 있습니다.버퍼 오버런, 삭제된 메모리 액세스, 메모리 누수 등은 매우 쉽게 찾아서 수정할 수 있습니다.시중에는 여러 가지가 있으며, 2~3일을 들여 직접 작성하고 필요한 기능의 90%를 얻을 수도 있습니다.그것들 없이 앱을 작성한다면 작업을 필요 이상으로 훨씬 더 어렵게 만드는 것입니다.

간단한 이유가 더 있습니다 assert(ptr != NULL) 포인터를 한 번 언급하기 전에는 작동하지 않을 것입니다. 모든 유효하지 않은 포인터 (널로 시작된 것조차도)가 실제로 0과 같습니다.

먼저 여러 회원이있는 구조물이있는 경우를 고려하십시오.

struct mystruct {
    int first;
    int second;
    int third;
    int fourth;
};

포인터가있는 경우 ptr 에게 mystruct 그리고 당신은 접근하려고 노력합니다 ptr->second, 컴파일러는 ADS 4 (32 비트 정수를 가정)의 코드를 생성 할 것입니다. ptr 그리고 그 메모리 위치에 액세스하십시오. 만약에 ptr 0, 액세스 된 실제 메모리 위치는 4입니다. 여전히 유효하지 않지만 간단한 주장에 의해 잡히지 않습니다. (컴파일러는 주소를 합리적으로 확인할 수 있습니다. ptr 4를 추가하기 전에,이 경우 어설 션은 그것을 잡을 것입니다.)

둘째, 배열이있는 경우를 고려하십시오. struct mystruct 그리고 당신은 임의의 요소를 다른 기능으로 전달합니다. 배열의 두 번째 요소에 액세스하려고하면 첫 번째 포인터 너머의 16 바이트에서 시작됩니다. 합법적 인 포인터 산술을 포착하지 않고 컴파일러가 모든 경우에 신뢰할 수있는 일을 합리적으로 기대할 수있는 방법은 없습니다.

실제로하고 싶은 것은 운영 체제와 하드웨어를 사용하여 유효하지 않은 메모리 액세스를 잡고 응용 프로그램을 죽인 다음 필요한 디버깅 정보를 얻는 방법을 파악하는 것입니다. 가장 쉬운 방법은 단순히 디버거 안에서 실행하는 것입니다. Linux에서 GCC를 사용하는 경우 참조하십시오 내 C ++ 앱이 충돌 할 때 스택 테이스를 생성하는 방법. 다른 컴파일러와 동일한 작업을 수행하는 방법이 비슷하다고 생각합니다.

이미 OS의 일부인 assert (prt! = null)와 동일합니다. 그렇기 때문에 0 주소에서 중요한 중요한 데이터를 덮어 쓰는 대신 Segfault를 얻은 다음 실제로 시스템을 엉망으로 만들었습니다.

실행 파일에 대한 기호 파일이 있으면 충돌 위치를 줄 번호에 매핑할 수 있습니다.다른 사람들이 언급한 것처럼 디버거에서 실행 중인 경우 디버거가 이 작업을 수행합니다.Visual C++는 프로그램이 충돌할 때 충돌이 발생한 프로세스에 디버거를 연결하여 문제가 있는 위치를 확인할 수 있는 "적시" 디버깅도 제공합니다.

그러나 Visual C++가 설치되지 않은 컴퓨터에서 이 기능을 사용하려는 경우 일부 코딩을 통해 수행할 수 있습니다.다음을 사용하여 예외 처리기를 설정할 수 있습니다. SetUnhandledExceptionFilter 프로그램이 충돌할 때 호출됩니다.처리기에서 예외 기록을 보고 사용할 수 있습니다. SymGetLineFromAddr64 실행 중인 소스 라인을 확인합니다."디버그 도움말" 라이브러리에는 모든 종류의 정보를 추출할 수 있는 많은 기능이 있습니다.보다 MSDN의 기사, 그리고 다음 기사에도 나와 있습니다. www.debuginfo.com.

그래서 당신은 시스템이 오류를 던지기 전에 오류를 던져야한다고 말하고 있습니다 .... 임박한 오류에 대해 경고합니까?

요점은 무엇입니까? 내가 segfault를 얻을 때, 나는 그것이 내가 segfault를 받았다는 것을 알고 있습니다. 먼저 "당신은 이제 segfault를 얻을 것"이라는 별도의 메시지가 필요하지 않습니다.

여기서 요점을 완전히 놓치고 있습니까? :피

편집하다:편집 할 때 의미하는 바를 볼 수 있지만 구현하기는 쉽지 않습니다. 문제는 컴파일러 나 언어 또는 런타임이 잘못된 포인터에 액세스하면 어떻게 해야하는지 결정하는 것이 아니라는 것입니다. 언어는 공식적으로 이에 대한 약속이나 보장을하지 않습니다. 대신, OS는 어떤 줄 번호가 문제를 일으킨 지 알지 못하고 디버그 실행 파일이라는 것을 알지 못하고 오류를 해제합니다. 이 오류가 말하는 유일한 것은 "주소 X에 액세스하려고 시도했는데 그것을 허용 할 수 없습니다. 죽습니다"입니다. 컴파일러는 이것으로 무엇을해야합니까?

그렇다면 누가이 유용한 오류 메시지를 생성해야합니까? 그리고 어떻게? 컴파일러는 할 수 있지만 포장 모든 오류 처리의 단일 포인터 액세스, SEGFAULT/액세스 위반이 발생하면 대신 어설 링을 트리거합니다. 문제는 이것이 될 것입니다 말도 안되게 느립니다. "릴리스에는 너무 느리게"가 아니라 "너무 느리게 사용하기에는 너무 느립니다". 또한 컴파일러가 액세스 할 수 있다고 가정합니다 모두 당신이 호출하는 코드. 타사 라이브러리에서 함수를 호출하면 어떻게됩니까? 컴파일러가 해당 라이브러리에 대한 코드를 생성하지 않기 때문에 오류 처리 코드로 랩핑 할 수없는 내부에 접근 할 수 있습니다.

OS ~할 수 있었다 관련 기호 파일을 기꺼이로드 할 수 있다고 가정하고, 어떻게 든 디버그 실행 파일을 실행하는지 여부를 감지하는 등을 인쇄 할 수 있습니다. 과잉 가입에 대해 이야기하십시오. 이것은 OS의 직업이 아닙니다.

그리고 마지막으로, 당신은 이것을함으로써 무엇을 얻을 것인가? 왜 단순히 디버거를 발사하지 않습니까? 이와 같은 일이 발생하면 자동으로 파손되어 정확한 줄 번호와 다른 모든 것을 제공합니다.

그것 ~할 수 있었다 완료되지만 끔찍하게 복잡하고 컴파일러와 OS를 모두 포함하며 이점은 매우 작을 것입니다. 디버거가 이미 알려줄 수 있다는 정보를 알려주는 팝업 상자가 있습니다. 그리고 그 정보를 사용하여, 당신은 ... 디버거를 발사 할 것입니다 그래도 무엇이 잘못되었는지 알아 내기 위해.

좋은 생각이지만 하나의 특별한 경우에만. 그것은 당신이 funciton 포인터를 비두용하기 전에입니다. 그 이유는 디버거가 항상 시작되기 때문입니다. 그러나 널 함수 포인터를 피한 후 스택이 촬영되었습니다. 따라서 불쾌한 코드를 찾는 데 문제가 있습니다. 발신자가 호출하기 전에 확인하면 디버거가 전체 스택을 줄 수 있습니다.

보다 일반적인 점검은 포인터가 실행 가능한 메모리를 가리키는 지 확인하는 것입니다. NULL은 그렇지 않지만 많은 운영 체제도 CPU 기능을 사용하여 특정 메모리 세그먼트를 실행 불가능하게 만듭니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top