2

我有一个类,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 类声明之前被编译——因此这就是问题出现的地方。

4

9 回答 9

7

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

我想到的一些可能性:

  1. 您正在引用另一个 markerStart 变量,它会隐藏您感兴趣的成员变量。
  2. 您在 C 的基类的方法中计算 sizeof。sizeof() 仅测量静态类型,而不测量动态类型。
  3. 您在某处违反了单一定义规则,并且有两个不同版本的 C 类(可能是通过头文件中的一些 #ifdef 在两个翻译单元中以不同方式解释)。

如果没有更多信息,我会选择违反 ODR。这些可能是阴险的,并且在编译或链接时无法检测到。

于 2009-04-08T12:27:09.630 回答
5

正如 Pontus 所指出的,您可能以某种方式违反了单一定义规则。您可能已经在两个翻译单元中包含了带有 C 类定义的头文件,而其他代码(通常在前面的头文件中)更改了 C 类定义的解释方式,使其在两个翻译中具有不同的大小单位。

一种可能性是,您不小心更改了默认成员对齐方式(通过编译器的命令行参数或通过源代码中的 #pragma),因此两个不同的翻译单元认为结构具有不同的大小,因为数量不同填充(大多数 x86 编译器默认对齐 4 字节边界,但允许您根据需要请求对齐 1 字节边界)。查看其他头文件中的 #pragma 更改缺少以下 #prama 的默认对齐方式以将其恢复为先前的值(您没有指定什么编译器,所以我无法给出细节)。

于 2009-04-08T14:27:17.750 回答
1

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

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

于 2009-04-08T12:53:44.417 回答
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()在那个时候打印吗(假设这个类有一些虚函数)?

于 2009-04-08T13:05:14.170 回答
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;
}

如果您编译上述两个文件并链接它们,您将不会收到任何错误或警告。但是,当您运行应用程序时,您会发现 DoSomethingWithC 破坏了 array_of_c [1],即使它的参数是 array_of_c [0]。

所以你的问题可能是一个源文件以一种方式看待类,而另一个文件以不同的方式看待它。如果相关性检查失败,就会发生这种情况。

尝试强制重建所有。如果可行,那么您将需要查看依赖项失败的原因(例如,DevStudio 有时可能会出错)。

于 2009-04-08T13:51:23.250 回答
1

您的班级定义或项目设置最近是否发生了变化?我见过这样的问题,我得到了像这样绝对荒谬的行为,原因是链接过时的目标文件。目标文件不再匹配源文件。尝试干净和完整的重建。

于 2009-04-08T16:40:23.530 回答
0

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

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

于 2009-04-08T12:19:31.347 回答
0

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

于 2009-04-08T12:50:36.640 回答
0

检查 *this 是否真的指向类的开始,而不是通过错误指针进行调用。

于 2009-04-08T16:54:17.413 回答