sizeof (* this) дает неправильное значение - PullRequest
2 голосов
/ 08 апреля 2009

У меня есть класс, 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 ]

7 голосов
/ 08 апреля 2009

Вам нужно опубликовать больше кода - еще лучше было бы, если бы вы могли сократить его до минимального определения класса, где возникает ваша аномалия. Это само по себе, вероятно, поможет вам определить, что происходит.

Некоторые возможности, которые происходят со мной:

  1. Вы ссылаетесь на другую переменную markerStart, которая затеняет интересующую вас переменную-член.
  2. Вы вычисляете sizeof в методе базового класса C. sizeof () измеряет только статический тип, а не динамический тип.
  3. Вы где-то нарушили One Definition Rule и имеете две разные версии класса C (возможно, через некоторый #ifdef в заголовочном файле, который по-разному интерпретируется в двух единицах перевода).

Без дополнительной информации я бы пошел за нарушение ODR. Они могут быть коварными, и их невозможно обнаружить при компиляции или компоновке.

5 голосов
/ 08 апреля 2009

Как указал Понт, вы, вероятно, как-то нарушили правило одного определения. Вы, вероятно, включили файл заголовка с определением класса C в две единицы перевода, где другой код, часто в предыдущем файле заголовка, изменяет способ интерпретации определения класса C, чтобы он имел различный размер в двух переводах ед.

Одна из возможностей заключается в том, что вы случайно изменили выравнивание элементов по умолчанию (либо через аргумент командной строки для компилятора, либо через #pragma в исходном коде), так что два разных модуля перевода считают, что структура имеет разный размер, потому что разного количества отступов (большинство компиляторов x86 по умолчанию выравнивают по 4-байтовым границам, но при необходимости позволяют запрашивать выравнивание по 1-байтовым границам). В других заголовочных файлах найдите #pragma, изменяющую выравнивание по умолчанию, в котором отсутствует следующая #prama, чтобы восстановить его до прежнего значения (какой компилятор не указан, поэтому я не могу дать подробности).

1 голос
/ 08 апреля 2009

Изменились ли вообще определения вашего класса или настройки проекта в последнее время? Я видел такие проблемы, когда я получал абсолютно абсурдное поведение, подобное этому, и причиной было связывание устаревших объектных файлов. Объектные файлы больше не соответствуют исходным файлам. Попробуйте чистую и полную перестройку.

1 голос
/ 08 апреля 2009

Проблема, с которой вы сталкиваетесь, вероятно, связана с некоторыми ошибками зависимости, а не с ошибками в компиляторе (компиляторы используются сотнями тысяч разработчиков, и если бы возникла такая проблема, то она уже была бы найдена).

Рассмотрим простой проект со следующими двумя файлами. Файл 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 иногда может ошибиться).

1 голос
/ 08 апреля 2009

Нет ли у вас странной проблемы с приведением указателя в коде? Что-то похожее на это?

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 голос
/ 08 апреля 2009

Есть ли в вашем классе виртуальные методы? или это происходит от класса с виртуальными методами? Или у вашего класса множественное наследование?

Ответ зависит от того, какой компилятор вы используете, компилятор может хранить указатель (и) на виртуальную таблицу (ы). Это на самом деле деталь реализации, но пока она работает так же, как стандарт, она может хранить любые данные с каждым объектом.

0 голосов
/ 08 апреля 2009

Убедитесь, что * это действительно указывает на начало класса, а не на вызов через неверный указатель.

0 голосов
/ 08 апреля 2009

Может ли это быть проблемой "Class Selling"?

0 голосов
/ 08 апреля 2009

Класс может иметь только 0x216 байт, но следующий объект, конечно, 0x218 байт после запуска вашего первого объекта. Ваши объекты, очевидно, выровнены по 4-байтовым границам памяти, что является значением по умолчанию.

Вы должны искать в другом месте, чтобы узнать, где ваша память забита. Это определенно не инструкция markerStart = false.

...