sizeof(* this)给出错误的值

时间:2009-04-08 12:11:06

标签: c++ assembly sizeof

我有一个类,C。C有一个成员变量,声明为:bool markerStart;

从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,使用数据断点)。下一个类的第一个字节被设置为零,这会弄乱它的vftbl指针。

注意:获取markerStart的地址并从中减去它,产生0x211!

有人能告诉我从哪里开始寻找解决这个问题的线索吗?

更新:感谢所有帮助。 没有代码,你几乎不可能解决问题。我正在寻找的是关于从哪里开始寻找的提示。大多数人提供了很好的提示,谢谢!

我终于找到了问题。在这种情况下,已在一个类中设置了对齐,并且在关键代码块之后未正确重置。具有错误对齐的类恰好在C类声明之前被编译 - 因此这就是问题出现的地方。

9 个答案:

答案 0 :(得分:7)

您需要发布更多代码 - 如果您可以将其修剪到异常发生的最小类定义,那就更好了。这本身可能会帮助您确定正在发生的事情。

我遇到的一些可能性:

  1. 您正在引用另一个标记您感兴趣的成员变量的markerStart变量。
  2. 您计算C基类方法中的sizeof.sizeof()仅测量静态类型,而不测量动态类型。
  3. 你已经在某个地方打破了One Definition Rule,并且有两个不同版本的C类(可能是通过头文件中的某些#ifdef,在两个翻译单元中进行了不同的解释)。
  4. 如果没有更多信息,我会选择ODR违规行为。这些可能是阴险的,在编译或链接时无法检测到。

答案 1 :(得分:5)

正如Pontus所指出的那样,你可能会以某种方式打破一个定义规则。您可能在两个翻译单元中包含了带有C类定义的头文件,其中其他代码(通常在前面的头文件中)更改了C类定义的解释方式,因此它在两个翻译中具有不同的大小单元。

一种可能性是,您无意中更改了默认成员对齐(通过命令行参数到编译器或通过源中的#pragma),以便两个不同的转换单元认为结构具有不同的大小,因为不同数量的填充(大多数x86编译器默认为4字节边界上的对齐,但允许您根据需要请求1字节边界上的对齐)。在其他头文件中查找#pragma,更改缺少以下#prama的默认对齐,将其恢复为之前的值(您不指定编译器,因此我无法给出具体信息)。

答案 2 :(得分:1)

你的班级有虚拟方法吗?或者它是从具有虚拟方法的类派生的?或者你的班级有多重继承?

答案取决于您使用的编译器,编译器可以将指针存储到虚拟表中。这实际上是一个实现细节,但只要它与标准相同,它就可以为每个对象存储任何类型的数据。

答案 3 :(得分: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()(假设该类有一些虚函数)吗?

答案 4 :(得分: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有时会出错)。

答案 5 :(得分:1)

最近是否更改了课程定义或项目设置?我已经看到过像这样的问题,我在这里得到了绝对荒谬的行为,原因是陈旧的目标文件被链接。目标文件不再与源文件匹配。尝试干净完整的重建。

答案 6 :(得分:0)

该类可能只有0x216字节,但是在第一个对象启动后,下一个对象当然是0x218字节。您的对象显然与4字节内存边界对齐,这是默认值。

你必须到别处寻找你的记忆被破坏的地方。它绝对不是'markerStart = false'指令。

答案 7 :(得分:0)

这可能是“类切片”问题的一个实例吗?

答案 8 :(得分:0)

检查*这确实指向了类的开头,而不是通过错误指针调用。