2014-10-22 6 views
16

최근에 완전히 이해할 수없는 함수 포인터와 관련하여 C++에서 비헤이비어가 발생했습니다. 나는 경험 많은 동료와 마찬가지로 Google에 도움을 요청했지만 도움을 줄 수는 없었다.C++에서 함수 포인터를 캐스팅 할 때 이상한 동작이 발생했습니다.

다음 코드는이 신비의 동작을 전시 :

class MyClass{ 
private: 
    int i; 

public: 
    MyClass(): i(0) {} 
    MyClass(int i): i(i) {} 

    void PrintText() const { std::cout << "some text " << std::endl;} 
}; 

typedef void (*MyFunction) (void*); 

void func(MyClass& mc){ 
    mc.PrintText(); 
} 

int main(){  
    void* v_mc = new MyClass; 
    MyFunction f = (MyFunction) func; //It works! 
    f(v_mc); //It works correctly!!! 

    return 0; 
} 

그래서, 처음에는 (특히,이 멤버 메소드 PrintText이다) 나중에 사용하는 간단한 클래스를 정의합니다. 그런 다음 이름 개체 void (*) (void*)MyFunction - 하나의 void* 매개 변수가있는 함수에 대한 포인터로 정의하고 값을 반환하지 않습니다.

그런 다음 MyClass 개체에 대한 참조를 허용하는 함수 func()을 정의하고 해당 메서드 PrintText을 호출합니다.

마지막으로 마술은 주 기능에서 발생합니다. 동적으로 새로운 MyClass 개체에 대한 메모리를 할당하여 반환 된 포인터를 void*에 캐스팅했습니다. 그런 다음 func() 포인터를 MyFunction 포인터로 캐스팅합니다.이 모든 컴파일을 기대하지는 않지만 그렇습니다. 기본 기능 (func()가) MyClass 객체에 대한 참조를 허용하더라도

그리고 마지막으로, 나는 void* 인수으로 새로운 개체를 호출. 모든 것이 올바르게 작동합니다!

이 코드를 Visual Studio 2010 (Windows)과 XCode 5 (OSX)로 컴파일하려고 시도했는데 같은 방식으로 작동합니다. 아무런 경고도보고되지 않습니다. 이 작동하는 이유는 상상이 C + +를 참조 실제로 장면 뒤에 포인터로 구현되어 있지만 이것은 설명이 아닙니다.

누군가가이 동작을 설명 할 수 있기를 바랍니다.

+1

코드에 버그 (다른 유형을 가리 키도록 캐스팅 된 포인터를 참조 해제)가 있습니다. 버그를 수정하면 신비가 사라질 것입니다. 예, 버그가있는 코드는 예상 한대로 작동하지 않습니다. 그것이 버그를 발견했을 때 수정해야하는 이유입니다. –

+0

캐스트가 경고/오류를 숨 깁니다. 이것은 '오류 : void (*) (MyClass &)'에서 'MyFunction {aka void (*) (void *)}'[-fpermissive] 로의 잘못된 변환입니다. 컴파일러의 진단을 무시하지 마십시오. –

+0

@remyabel 음, 캐스트가 완벽하게 합법적입니다 (그래서'reinterpret_cast'가 될 것입니다). – Angew

답변

31

공식적인 설명은 간단합니다. 정의되지 않은 동작은 정의되지 않았습니다. 다른 함수 유형에 대한 포인터를 통해 함수를 호출 할 때 정의되지 않은 동작이며 프로그램에서 합법적으로 아무 것도 할 수 있습니다 (충돌, 작동 표시, 온라인 피자 주문 ... anyting goes).

발생하는 문제에 대한 추론을 시도해 볼 수 있습니다.아마도 다음 요소 중 하나 이상의 조합 일 것입니다.

  • 컴파일러는 내부적으로 참조를 포인터로 구현합니다.
  • 플랫폼에 따라 모든 포인터의 크기와 이진 표현은 동일합니다.
  • PrintText()*this에 전혀 액세스하지 않으므로 컴파일러에서 mc의 값을 무시하고 안에 PrintText() 함수를 호출하면됩니다.

그러나, 당신은 당신이 현재 당신이 현재의 플랫폼 컴파일러 버전에 달이 단계에서 설명 한 문제가 발생하는 동안,이 아무런 이유없이 언제든지 변경 될 수 있음을 기억해야한다 (다른 최적화를 유발하는 주변 코드의 변경과 같은). 정의되지 않은 동작은 정의되지 않았 음을 기억하십시오. &funcMyFunction에 당신이 캐스팅 이유에


은 - 표준 명시 적으로 허용하는 (하는 reinterpret_cast와 C 스타일의 캐스팅이 맥락에서 해석되는). 합법적으로 함수에 대한 포인터를 함수 유형에 대한 다른 포인터로 캐스트 할 수 있습니다. 그러나 합법적으로 할 수있는 유일한 방법은 원래 유형으로 되돌 리거나 캐스트하는 것입니다. 앞에서 말했듯이, 잘못된 유형의 함수 포인터를 호출하면 정의되지 않은 동작입니다.

+10

mmhm, 나는 컴파일러가 나를 위해 피자를 주문할 수 있다고 확신 할 수 있다면 나쁜 코드를 작성하는 것에 신경 쓰지 않을 것입니다. – Thomas

7

I hope someone can explain this behavior.

동작은 정의되지 않습니다.

MyFunction f = (MyFunction) func; //It works! 

당신은 내가 생각이 경우 reinterpret_cast 것과 같은 효과가 C 스타일 캐스트를 사용하기 때문에 그것은 "작동". static_cast을 사용했거나 전혀 캐스트하지 않았다면 컴파일러는 실수를 경고하고 실패합니다. 잘못 해석 된 함수 포인터를 호출하면 정의되지 않은 동작이 발생합니다.

2

우연히 만 작동합니다. 컴파일러가 작동하도록 보장되지는 않습니다. 뒤에서는 컴파일러가 참조를 포인터로 취급하므로 대체 함수 서명이 작동합니다.

+1

나는 그가 "일"하는 데있어서 운이 좋지 않다고 말할 것이다. – user2079303

+0

편집 됨 : 당신 말이 맞습니다. – tenfour

0

미안하지만, 왜 이상한 행동이라고 부르는 지 모르겠다. 여기서 달주기에 의존하는 정의되지 않은 동작은 보이지 않는다. C에서 함수 포인터를 사용하는 방법이다.

일부 디버그 출력을 추가하면 개체에 대한 포인터가 모든 호출에서 동일하게 유지되는 것을 볼 수 있습니다.

void PrintText() const { std::cout << "some text " << this << std::endl;} 
                 ^^^^ 
void func(MyClass& mc){ 
    std::cout << (void *)&mc << std::endl; 
         ^^^ 
void *v_mc = new MyClass; 
std::cout << (void *)v_mc << std::endl; 
        ^^^^ 
관련 문제