Ошибка в С++-коде из-за ошибки undefined или компилятора? - программирование
Подтвердить что ты не робот

Ошибка в С++-коде из-за ошибки undefined или компилятора?

Я испытываю странные сбои. И я задаюсь вопросом, является ли это ошибкой в ​​моем коде или компиляторе. Когда я компилирую следующий код на С++ с Microsoft Visual Studio 2010 в качестве оптимизированной сборки выпуска, он падает в отмеченной строке:

struct tup { int x; int y; };

class C 
{
public:
  struct tup* p;

  struct tup* operator--() { return --p; }
  struct tup* operator++(int) { return p++; }

  virtual void Reset() { p = 0;}
};

int main ()
{
  C c;
  volatile int x = 0;
  struct tup v1;
  struct tup v2 = {0, x};

  c.p = &v1;
  (*(c++)) = v2;

  struct tup i = (*(--c));   // crash! (dereferencing a NULL-pointer)
  return i.x;
}

Взглянув на разборку, очевидно, что он должен сработать:

int _tmain(int argc, _TCHAR* argv[])
{
00CE1000  push        ebp  
00CE1001  mov         ebp,esp  
00CE1003  sub         esp,0Ch  
  C c;
  volatile int x = 0;
00CE1006  xor         eax,eax  
00CE1008  mov         dword ptr [x],eax  
  struct tup v1;
  struct tup v2 = {0, x};
00CE100B  mov         ecx,dword ptr [x]  

  c.p = &v1;
  (*(c++)) = v2;
00CE100E  mov         dword ptr [ebp-8],ecx  

  struct tup i = (*(--c));
00CE1011  mov         ecx,dword ptr [x]  
00CE1014  mov         dword ptr [v1],eax  
00CE1017  mov         eax,dword ptr [ecx]  
00CE1019  mov         ecx,dword ptr [ecx+4]  
00CE101C  mov         dword ptr [ebp-8],ecx  
return i.x;
}
00CE101F  mov         esp,ebp  
00CE1021  pop         ebp  
00CE1022  ret  

При смещении 00CE1008 он записывает 0 в x.

При смещении 00CE100B он считывает x (0) в ecx

При смещении 00CE1017 это разыгрывает то, что 0-указатель.

Я вижу две возможные причины:

  • В моем коде есть какой-то тонкий (или не такой тонкий?) случай поведения undefined и компилятор "оптимизирует" это поведение undefined в результате сбоя.

  • или есть ошибка компилятора

Кто-нибудь видит, что может вызвать проблему?

Спасибо,

Джонас

РЕДАКТИРОВАТЬ: Чтобы ответить на комментарии относительно "указателя на недопустимое местоположение"

Если я изменил v1 на struct tup v1[10]; и установил c.p = &v1[0];, тогда не будет указателя на недопустимое местоположение. Но я все еще могу наблюдать такое же поведение. Разборки выглядят незначительно по-разному, но по-прежнему происходит сбой, и он все еще вызван загрузкой 0 в ecx и разыменованием его.

EDIT: Заключение

Итак, возможно, это ошибка. Я узнал, что авария исчезает, если я изменяю

struct tup* operator--() { return --p; }

к

struct tup* operator--() { --p; return p; }

Как сообщает bames53, авария не возникает в VS2011 и приходит к выводу, что она должна быть исправлена.

Тем не менее, я решил записать эту ошибку по двум причинам:

  • Ошибка может по-прежнему присутствовать в VS2011. Возможно, оптимизатор просто изменился так, что мой код больше не вызывает ошибку. (ошибка кажется очень тонкой, она не возникает, когда я удаляю volative или virtual void Reset())

  • Я хочу знать, может ли мой обходной путь надежным способом исключить сбои или если изменения кода в других местах могут повторно ввести ошибку.

Вот ссылка:

https://connect.microsoft.com/VisualStudio/feedback/details/741628/error-in-code-generation-for-x86

4b9b3361

Ответ 1

Код в порядке. Это ошибка компилятора.

В коде *(c++) = v2 будет выполняться пост-инкремент c.p с исходным значением. Это значение было присвоено в предыдущей строке и &v1. Таким образом, в действительности это делает v1 = v2;, что отлично.

c.p теперь ведет себя как один конец прошлого массива из одного элемента, который содержит только v1, согласно §5.7p4 стандарта:

Для этих операторов [ + и -] указатель на объект nonarray ведет себя так же, как указатель на первый элемент массив длиной один с типом объекта в качестве его элемента тип.

Затем *(--c) перемещает этот указатель обратно на &v1 и разыгрывает его, что тоже прекрасно.

Ответ 2

Это не должно быть ошибкой UB или компилятора. Это не может быть связано с тем, как VS2010 был произведен.

Строго говоря, ваша программа демонстрирует четко определенное поведение. Однако это может быть только в соответствии с новейшим стандартом С++. VS2010 применяется только в отношении проекта стандарта, который, возможно, не включал это положение. Если это не так, то ваш код не является UB, но VS не является неправильным для создания UB, поскольку это были требования времени, которое было сделано.

Конечно, если было законно рассматривать объекты стека как массив одного объекта в С++ 03, то это ошибка компилятора.

Изменить: если вы все еще получаете крах для массива, как вы заявляете, то это определенно ошибка компилятора.

Ответ 3

Вы берете &v1 в c.p и позже, используя оператор ++, который вы продвигаете. Вы не можете полагаться на упорядочение стека, поэтому приходит undefined поведение ((&v1)+1 != &v2)