2009-04-08 4 views
2

클래스가 있습니다. C. C에 선언 된 멤버 변수가 있습니다. bool markerStart;sizeof (* this) 값이 잘못됨

C 내에서 sizeof (* this)를 호출하면 0x216 바이트의 값이 제공됩니다.

C 내의 다른 곳에서 : markerStart = false;

markerStart를 false로 설정하는 대신이 호출은 실제로 메모리의 다음 클래스 시작을 방해합니다!

markerStart = false; 
06FB6B7F mov   eax, dword ptr [this] 
06FB6B78 mov   byte ptr [eax+218h], 0 

두 번째 이동 명령이 + 0x218 제로에 바이트를 설정되어 있지만 클래스는 0x216 바이트이기 때문에,이 메모리를 건드리지된다

디스 어셈블 된 코드를 보면, 내가 발견!

의견에 대한 응답으로, 그것은 확실히 markerStart = false 명령입니다. 나는 디스어셈블러 뷰와 메모리 뷰 (그리고 데이터 브레이크 포인트를 사용하여 Windbg 사용)에서 일어나는 것을 볼 수 있습니다. 다음 클래스의 첫 번째 바이트는 0으로 설정되고 vftbl 포인터는 엉망이됩니다.

참고 : markerStart의 주소를 가져 와서 이것을 빼면 0x211이 생성됩니다!

누구든지 내게이 문제를 해결하기 위해 어디서부터 시작해야할지 알려줄 수 있습니까?

업데이트 : 모든 도움에 감사드립니다. 코드가 없으면 어느 누구도 문제를 해결할 수 없었습니다. 내가 찾고 있던 것은 어디서부터 시작해야할지에 대한 암시였습니다. 여러분 중 대다수가 훌륭한 힌트를 주셨습니다. 감사합니다!

마침내 문제가 발견되었습니다. 이 경우 정렬은 하나의 클래스에서 설정되었으며 중요한 코드 블록 다음에 올바르게 재설정되지 않았습니다. 잘못 정렬 된 클래스는 C 클래스 선언 바로 전에 컴파일되므로 문제가 발생한 부분입니다.

+1

문제를 설명하는 실제 C++ 코드를 게시하십시오. –

+0

실제 코드를 게시 할 수 있습니까? C의 정의는 좋은 시작이 될 것입니다 – PaulJWilliams

+0

아마도 218! = 0x218 – Skizz

답변

5

Pontus에 의해 지적 되었 듯이, 당신은 아마도 어떻게 든 하나의 정의 규칙을 어겼을 것입니다. 두 개의 번역 단위로 된 C 클래스의 정의가 포함 된 헤더 파일을 포함했을 것입니다. 다른 헤더는 종종 앞의 헤더 파일에서 C 클래스의 정의가 해석되는 방식을 변경하여 두 개의 변환에서 다른 크기를 갖습니다 단위.

두 개의 다른 번역 단위가 구조가 다른 크기라고 생각하도록 실수로 기본 멤버 정렬을 컴파일러의 명령 줄 인수 또는 원본의 #pragma를 통해 변경했습니다. 패딩의 양이 다르기 (대부분의 x86 컴파일러는 기본적으로 4 바이트 경계에서 정렬되지만, 필요에 따라 1 바이트 경계에서 정렬을 요청할 수 있습니다). 다른 헤더 파일에서 #pragma가 다음 값을 잃어버린 기본 정렬을 변경했는지 확인하십시오. #prama는 이전 값으로 복원합니다 (어떤 컴파일러를 지정하지 않으므로 구체적인 내용을 제공 할 수 없습니다).

+0

좋은 스티븐. 나는 당신의 대답을 읽기 전에 문제를 발견했지만 머리에 못을 박았습니다. 건배! – Rob

0

클래스에는 0x216 바이트 만있을 수 있지만 다음 개체는 물론 첫 번째 개체가 시작된 후 0x218 바이트입니다. 당신의 객체는 분명히 4 바이트 메모리 경계에 정렬되어 있는데, 이것이 기본값입니다.

다른 곳을보고 기억이 쌓여있는 곳을 찾아야합니다. 분명히 'markerStart = false'명령이 아닙니다.

+0

생성 된 코드 [eax + 218]는 어떻게됩니까? 객체가 16 바이트 경계에 정렬되지 않으면 덮어 쓰여질 다음 객체가됩니다. – Mike

+0

어쨌든 말이되지 않습니다. 객체 사이에는 패딩이 없습니다. (char *) (a + n) == (char *) (a) + N * sizeof (a). sizeof (C)가 0x216이면 컴파일러는 짝수 번째 주소에 C 객체를 배치 할 수 있습니다. 클래스의 정렬이 모든 멤버 정렬의 GCD가되는 것이 일반적입니다. 2는 괜찮아 보인다. – MSalters

7

더 많은 코드를 게시해야합니다. 예외가 발생하는 최소 클래스 정의로 정리할 수 있다면 더 좋을 것입니다. 그 자체로 아마 당신이 무슨 일이 일어나고 있는지 확인하는데 도움이 될 것입니다. 나에게 발생

일부 가능성 :.

  1. 당신은 당신이 관심있는 멤버 변수를 그림자 다른 markerStart 변수를 참조하는
  2. 당신은 C의 기본 클래스의 방법으로는 sizeof를 계산 sizeof()는 동적 유형이 아닌 정적 유형 만 측정합니다.
  3. 어딘가에 One Definition Rule을 깨고 두 개의 서로 다른 버전의 C 클래스 (두 개의 번역 단위로 다르게 해석되는 헤더 파일의 일부 #ifdef를 통해)가 있습니다.

자세한 정보가 없으면 ODR 위반으로 가고 싶습니다. 그러한 것들은 교활한 것이 될 수 있으며 컴파일이나 링크 할 때 탐지하기가 불가능합니다.

+0

당신은 옵션 2에 대한 권리입니다. 저는 sizeof (* this)를 VS watch 윈도우에서 직접 계산하여 0x216을 얻습니다. C의 메소드에 코드를 넣으면 sizeof (* this)는 0x220을 제공합니다. 이것은 여전히 ​​[eax + 218]가 다음 수업을 clobbering하는 이유를 설명하지 않습니다! 감사합니다. Rob – Rob

+0

IDE와 비슷한 것으로 들리므로 혼란스러워합니다. 나는 너라면 ODR 위반 가능성 (옵션 3)에 대해 오랫동안 열심히 살펴볼 것입니다. 여러 .h 파일? 직접 #ifdef? 멤버 변수 선언을 통한 간접 #ifdef 의존성? 다른 매크로 속임수? –

+0

ODR 위반과 마찬가지로 개체 파일 중 하나를 잘못 작성하여 최종 실행 파일에서 내부적으로 클래스가 일치하지 않는 경우가 드물게 발생합니다. – Mike

0

"Class slicing"문제의 인스턴스 일 수 있습니까?

1

클래스에 가상 메소드가 있습니까? 또는 가상 메소드가있는 클래스에서 파생됩니까? 또는 클래스에 다중 상속이 있습니까?

대답은 사용중인 컴파일러에 따라 다르며 컴파일러는 가상 테이블에 포인터를 저장할 수 있습니다. 이것은 실제로 구현 세부 사항이지만 표준과 동일하게 작동하는 한 각 객체에 모든 종류의 데이터를 저장할 수 있습니다.

1

코드에 이상한 포인터 캐스팅 문제가 있습니까? 이것과 비슷한 무엇인가?

struct A 
{ 
    int i; 
}; 

struct B : public A 
{ 
    int j; 
    void f() { j=0; } 
}; 

int main() 
{ 
    A x; 
    A* p=&x; 
    ((B*)p)->f(); 
    return 0; 
} 

실제로이 점을 확인해 볼 수 있습니까? C 인스턴스에서 메모리를 손상시키는 점을 실제로 확인할 수 있습니까? 그 시점에 typeid(*this).name()을 인쇄 할 수 있습니까 (클래스에 가상 함수가 있다고 가정)?

1

발생하는 문제는 아마도 컴파일러에 문제가있는 것보다 일부 종속성 오류 때문일 수 있습니다 (컴파일러는 수십만 명의 개발자가 사용하며 이와 같은 문제가있는 경우 지금까지 발견되었을 것입니다).

다음 두 파일을 사용하여 간단한 프로젝트를 생각해보십시오. 파일 a.cpp :

class C 
{ 
public: 
    C() : m_value (42) { } 
    void Print() { cout << "C::m_value = " << m_value << endl; } 
private: 
    int m_value; 
}; 

void DoSomethingWithC (C &c); 

void main (void) 
{ 
    C array_of_c [2]; 
    DoSomethingWithC (array_of_c [0]); 
    array_of_c [0].Print(); 
    array_of_c [1].Print(); 
} 

및 파일 b.cpp : 위의 두 파일을 컴파일하고이를 연결하면

class C 
{ 
public: 
    int a,b; 
}; 

void DoSomethingWithC (C &c) 
{ 
    c.b = 666; 
} 

당신이 오류 또는 경고를받지 않습니다. 그러나 응용 프로그램을 실행하면 인수가 array_of_c [0]인데도 DoSomethingWithC clobbers array_of_c [1]을 찾을 수 있습니다.

그래서 한 소스 파일이 클래스를보고 다른 파일이 다른 방식으로 보는 것이 문제 일 수 있습니다. 의존성 검사가 실패 할 경우 이런 일이 발생할 수 있습니다.

모두 다시 빌드 해보세요. 작동한다면 왜 의존성이 실패했는지 알아야합니다 (DevStudio는 때때로 잘못 이해할 수 있습니다).

1

클래스 정의 또는 프로젝트 설정이 최근에 변경 되었습니까?나는 이런 문제를 겪어 왔는데, 나는 이처럼 절대적으로 어리석은 행동을하고 있었는데, 그 원인은 부실한 목적 파일들이 연결되어 있다는 것이었다. 오브젝트 파일이 더 이상 소스 파일과 일치하지 않습니다. 깨끗하고 완전한 재구성을 시도하십시오.

0

* 이것이 실제로 잘못된 포인터를 통한 호출이 아니라 클래스의 시작을 가리키는 지 확인하십시오.