문제

다음 코드를 고려하십시오.

#include <iostream>

struct foo
{
    // (a):
    void bar() { std::cout << "gman was here" << std::endl; }

    // (b):
    void baz() { x = 5; }

    int x;
};

int main()
{
    foo* f = 0;

    f->bar(); // (a)
    f->baz(); // (b)
}

우리는 기대한다 (b) 해당 멤버가 없기 때문에 충돌합니다 x 널 포인터를 위해. 실제로, (a) 이후 충돌하지 않습니다 this 포인터는 사용되지 않습니다.

왜냐하면 (b) 불균형 this 포인터(*this).x = 5;), 그리고 this NULL은 NULL이 정의되지 않은 동작이라고 말하면서 프로그램이 정의되지 않은 동작에 들어갑니다.

하다 (a) 정의되지 않은 행동이 발생합니까? 두 기능 모두 (및 x) 정적입니까?

도움이 되었습니까?

해결책

둘 다 (a) 그리고 (b) 정의되지 않은 행동을 초래합니다. 널 포인터를 통해 멤버 함수를 호출하는 것은 항상 정의되지 않은 동작입니다. 기능이 정적 인 경우 기술적으로 정의되지 않지만 분쟁이 있습니다.


가장 먼저 이해해야 할 것은 널 포인터를 불신하는 것이 정의되지 않은 행동 인 이유입니다. C ++ 03에는 실제로 약간의 모호함이 있습니다.

하지만 "널 포인터를 디포 링하면 정의되지 않은 동작이 발생합니다." §1.9/4 및 §8.3.2/4의 메모에 언급되어 있으며 명시 적으로 명시되지는 않습니다. (메모는 규정이 없다.)

그러나 §3.10/2에서 그것을 추론하려고 시도 할 수 있습니다.

lValue는 물체 또는 기능을 나타냅니다.

탈환 할 때 결과는 lvalue입니다. 널 포인터 하지 않습니다 따라서 객체를 참조하십시오. 그러므로 LValue를 사용할 때 정의되지 않은 동작이 있습니다. 문제는 이전 문장이 명시되지 않았다는 것입니다. 그래서 lvalue를 "사용"하는 것은 무엇을 의미합니까? 더 공식적인 LValue-to-RValue 변환을 수행하는보다 공식적인 감각으로 사용하기 만하면?

그럼에도 불구하고 확실히 rvalue (§4.1/1)로 변환 할 수는 없습니다.

LValue가 지칭하는 객체가 T 형의 대상이 아니고 T에서 파생 된 유형의 객체가 아니거나 개체가 발의되지 않은 경우이 변환이 필요한 프로그램은 정의되지 않은 동작을 갖습니다.

여기서는 확실히 정의되지 않은 행동입니다.

모호성은 정의되지 않은 행동인지 여부에서 비롯됩니다. 그러나 사용하지 않습니다 유효하지 않은 포인터의 값 (즉, lvalue를 얻지 만 rvalue로 변환하지 않음). 그렇지 않다면 int *i = 0; *i; &(*i); 잘 정의되어 있습니다. 이것은 an입니다 활성 문제.

그래서 우리는 엄격한 "널 포인터 인 Dereference, 정의되지 않은 행동"보기 "보기 및 약한"greetered null pointer를 사용하고 정의되지 않은 동작을 얻으십시오 "보기가 있습니다.

이제 우리는 질문을 고려합니다.


예, (a) 정의되지 않은 행동을 초래합니다. 사실, if this 그러면 null입니다 함수의 내용에 관계없이 결과는 정의되지 않았습니다.

이것은 §5.2.5/3에서 나옵니다.

만약에 E1 "클래스 X에 대한 포인터"유형이 있습니다. 그런 다음 표현식이 있습니다. E1->E2 동등한 형태로 변환됩니다 (*(E1)).E2;

*(E1) 엄격한 해석으로 정의되지 않은 행동을 초래하고 .E2 그것을 rvalue로 변환하여 약한 해석을 위해 정의되지 않은 동작을 만듭니다.

또한 (§9.3.1/1)에서 직접 정의되지 않은 동작이라고합니다.

클래스 X의 비스틱 멤버 함수가 유형 X가 아닌 객체 또는 X에서 파생 된 유형에 대해 호출되는 경우 동작은 정의되지 않습니다.


정적 기능을 사용하면 엄격한 해석과 약한 해석이 차이를 만듭니다. 엄격하게 말하면, 그것은 정의되지 않았습니다.

정적 멤버는 클래스 멤버 액세스 구문을 사용하여 참조 할 수 있으며,이 경우 객체 표현이 평가됩니다.

즉, 그것은 마치 비 정적 인 것처럼 평가되었으며 우리는 다시 한 번 널 포인터를 불러 일으 킵니다. (*(E1)).E2.

그러나 왜냐하면 E1 약한 해석을 사용하면 통화가 잘 정의되어 있으면 정적 멤버 기능 호출에 사용되지 않습니다. *(E1) LValue가 발생하면 정적 기능이 해결되며 *(E1) 폐기되고 함수가 호출됩니다. LValue-to-RValue 변환이 없으므로 정의되지 않은 동작이 없습니다.

N3126 기준으로 C ++ 0x에서 모호성이 남아 있습니다. 지금은 안전하십시오 : 엄격한 해석을 사용하십시오.

다른 팁

분명히 정의되지 않은 것은 그 의미입니다 정의되지 않았습니다, 그러나 때로는 예측할 수 있습니다. 내가 제공하려는 정보는 확실히 보장되지 않기 때문에 작업 코드에 의존해서는 안되지만 디버깅 할 때 유용 할 수 있습니다.

객체 포인터에서 함수를 호출하면 포인터를 피하고 UB를 유발할 수 있다고 생각할 수 있습니다. 실제로 함수가 가상이 아닌 경우 컴파일러는 포인터를 첫 번째 매개 변수로 전달하는 일반 기능 호출로 변환했습니다. 이것, 불신을 우회하고 호출 된 멤버 함수에 대한 시한 폭탄을 만듭니다. 멤버 함수가 멤버 변수 또는 가상 함수를 참조하지 않으면 실제로 오류없이 성공할 수 있습니다. 성공한 것은 "정의되지 않은"우주에 속한다는 것을 기억하십시오!

Microsoft의 MFC 기능 getsafehwnd 실제로이 행동에 의존합니다. 나는 그들이 담배를 피우는 것을 모른다.

가상 기능을 호출하는 경우 포인터는 vtable에 도달하기 위해 불쾌감을 주어야하며, 확실히 UB를 얻을 것입니다 (아마도 충돌이지만 보장이 없다는 것을 기억하십시오).

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