Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2013.09.01;
Скачать: CL | DM;

Вниз

Интересная ошибка.   Найти похожие ветки 

 
Дмитрий С ©   (2013-03-26 23:05) [0]

Час примерно потратил на вылавливание ошибки:

program Project1;

{$APPTYPE CONSOLE}

var
 Data: array of record
   S: String;
 end = nil;
 DataCount: Integer = 0;
 I: Integer;

procedure GrowData;
begin
 if DataCount >= Length(Data) then
   SetLength(Data, Length(Data) + 1);
end;

begin
 GrowData;
 Data[DataCount].S := "Foo";
 Inc(DataCount);
 with Data[DataCount] do
 begin
   GrowData;
   Data[DataCount].S := S;
   Inc(DataCount);
 end;
 SetLength(Data, DataCount);
 Writeln(Data[0].S); // "Foo"
 Writeln(Data[1].S); // "", but expected "Foo"
 Readln;
end.

Код естественно далек от реальности, но тем не менее. Можно в тесты включить вариацию на эту тему:)


 
KilkennyCat ©   (2013-03-26 23:10) [1]

а я сегодня у ST в I2C нашел два вхождения в бесконечный цикл. Код "заводской".


 
брат Птибурдукова   (2013-03-26 23:14) [2]

Inc(DataCount); with Data[DataCount] do — эта хрень даст непредсказуемый результат. EurekaLog ругнулась бы на обращение за границами массива


 
Игорь Шевченко ©   (2013-03-26 23:41) [3]


> Час примерно потратил на вылавливание ошибки:


незачем писать ерунду, чтобы потом в ней искать ошибки


 
Дмитрий С ©   (2013-03-26 23:52) [4]


> Игорь Шевченко ©   (26.03.13 23:41) [3]

Тут пример с ошибкой и если исправить в дельфи работает, как ни странно.

Программу писал на фрипаскале, не подумав, что изменение длины массива внутри блока with от элемента этого массива критично.


 
TUser ©   (2013-03-27 00:17) [5]

Это еще что. А вот что нам напечатает такой код? А если компилятор - fpc 2.6.0 с опцией -Mdelphi ? )))

program Project1;

{$APPTYPE CONSOLE}

var
Data: array of record
  S: String;
end = nil;
DataCount: Integer = 0;
I: Integer;

function GrowData: integer;
begin
if DataCount >= Length(Data) then
  SetLength(Data, Length(Data) + 1);
result := DataCount;
inc (DataCount);
end;

begin
GrowData;
Data[DataCount-1].S := "Foo";
with Data[GrowData] do
  S := Data[DataCount-2].S + "@";

for i := 0 to DataCount - 1 do
  Writeln(Data[i].S);
end.


 
KilkennyCat ©   (2013-03-27 00:19) [6]


> изменение длины массива внутри блока with

? я другую увидел.


 
Игорь Шевченко ©   (2013-03-27 00:26) [7]

Дмитрий С ©   (26.03.13 23:52) [4]

Незачем писать ерунду. Написанный код - ерунда.


 
брат Птибурдукова   (2013-03-27 08:43) [8]


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


 
Jeer ©   (2013-03-27 09:09) [9]

>с университетским образованием

О, да! Это гарантия непогрешимости :)


 
брат Птибурдукова   (2013-03-27 09:11) [10]

уж мы их душили-душили!..


 
RWolf ©   (2013-03-27 09:35) [11]


> "", but expected "Foo"

Foo там окажется разве что случайно.


 
брат Птибурдукова   (2013-03-27 09:40) [12]

до правильного результата осталось 6957495349 запусков


 
Rouse_ ©   (2013-03-27 10:33) [13]


> что изменение длины массива внутри блока with от элемента
> этого массива критично.

При чем тут это?

Тыж анализируй код:

GrowData;
Data[DataCount].S := "Foo";
Inc(DataCount);

размер массива равен единице и DataCount равен единице, соответственно вот эта строчка выход за пределы массива...
with Data[DataCount] do

соответственно и переменная S относящаяся к кривому элементу явно никак не может содержать ожидаемое тобой "Foo"

 with Data[DataCount] do
  GrowData;
  Data[DataCount].S := S;


поэтому код кривой и никакого там ожидаемой "Foo" во втором элементе быть не может.


 
Дмитрий С ©   (2013-03-27 12:31) [14]


> Rouse_ ©   (27.03.13 10:33) [13]

Да я написал что код неправильный, не раскрывает сути проблемы. К сожалению у меня не получилось воспроизвести в дельфи короткий вариант этой ошибки. А она заключалась в изменении длины массива внутри блока with от элемента этого массива. Видимо при изменении длины меняется его адрес, к чему with не готов, по крайней мере во FP.


 
Дмитрий С ©   (2013-03-27 12:47) [15]

А вот в таком виде воспроизводится:

program Project1;

{$APPTYPE CONSOLE}

var
 Data: array of record
   S: String;
 end = nil;
 DataCount: Integer = 0;
 I: Integer;
 TestString: String;

procedure GrowData;
begin
 if DataCount >= Length(Data) then
   SetLength(Data, Length(Data) + 128);
end;

procedure AddElement(NewValue: String);
begin
 GrowData;
 Data[DataCount].S := NewValue;
 Inc(DataCount)
end;

begin
 { Используем значительной длины строку }
 SetLength(TestString, 1024);
 for I := 1 to Length(TestString) do
   TestString[I] := "A";

 { Добавляем нулевой элемент}
 AddElement(TestString);

 with Data[0] do
   { Еще миллион другой элементов }
   for I := 0 to 1024 * 1024 do
     AddElement(S);

 Writeln(Length(Data[0].S)); // 1024
 Writeln(Length(Data[1].S)); // 1024
 Writeln(Length(Data[200].S)); // 1024
 Writeln(Length(Data[DataCount - 1].S)); // 0, but expected 1024

 Readln;
end.

Как видно из примера - результат получается непредсказуемый.


 
Rouse_ ©   (2013-03-27 15:51) [16]

А это уже ошибка компилера. Показываю:

Project2.dpr.36: with Data[0] do
0040613A A1D8774000       mov eax,[$004077d8]
0040613F 8BF0             mov esi,eax
Project2.dpr.38: for I := 0 to 1024 * 1024 do
00406141 BB01001000       mov ebx,$00100001
Project2.dpr.39: AddElement(S);
00406146 8B06             mov eax,[esi]
00406148 E8AFF2FFFF       call AddElement
Project2.dpr.38: for I := 0 to 1024 * 1024 do
0040614D 4B               dec ebx
0040614E 75F6             jnz $00406146


база рассчитывается заранее и хранится в регистре ESI
а потом запускается обычный цикл, который выполняет следующее:

Project2.dpr.39: AddElement(S);
00406146 8B06             mov eax,[esi]
00406148 E8AFF2FFFF       call AddElement
Project2.dpr.38: for I := 0 to 1024 * 1024 do
0040614D 4B               dec ebx
0040614E 75F6             jnz $00406146


из-за реаллока база массива уехала и ESI указывает на невалидные данные.

Что собственно грозит еще и AV, здесь просто повезло.

Наглядный пример того что использовать WITH нужно крайне осторожно (а вообще лучше вообще выкинуть нафиг эту конструкцию).


 
Дмитрий С ©   (2013-03-27 16:01) [17]


> Что собственно грозит еще и AV, здесь просто повезло.

По-моему AV лучше, чем молчаливо неправильно и опасно работать :)


 
Rouse_ ©   (2013-03-27 16:03) [18]

Если сильно захотеть можно и AV получить, надо просто заставить освободить менеджер памяти занятую массивом Data на первой итерации цикла страницу (думаю ручками можно поигравшись с выделением-освобождением больших и маленьких блоков в чрессполосицу). А так она просто в резерв в данном случае перекидывается.


 
Rouse_ ©   (2013-03-27 16:18) [19]

Хотя нет, вру, это не ошибка компилера - это документированная ситуация описанная в справке.


> If the interpretation of obj involves indexing arrays or
> dereferencing pointers, these actions are performed once,
>  before statement is executed.



 
картман ©   (2013-03-27 16:21) [20]


> > If the interpretation of obj involves indexing arrays
> or
> > dereferencing pointers, these actions are performed once,
>
> >  before statement is executed.

зачем? Не лучше ли было сделать with только синтаксической конструкцией?


 
TUser ©   (2013-03-27 16:22) [21]

А она заключалась в изменении длины массива внутри блока with от элемента этого массива. Видимо при изменении длины меняется его адрес, к чему with не готов, по крайней мере во FP.

Адрес, куда указывает Data (адрес первого элемента массива), действительно может меняться - если на старом месте места мало. Модифицируем код из [5]:

program Project1;

{$APPTYPE CONSOLE}

var
Data: array of record
 S: String;
end = nil;
DataCount: Integer = 0;
I: Integer;

function GrowData: integer;
begin
if DataCount >= Length(Data) then
 SetLength(Data, Length(Data) + 1);
result := DataCount;
inc (DataCount);
end;

begin
GrowData;
Data[DataCount-1].S := "Foo";
writeln (longint(pointer(@Data[0])));
with Data[GrowData] do begin
 writeln (longint(pointer(@Data[0])));
 S := Data[DataCount-2].S + "@";
 end;

for i := 0 to DataCount - 1 do
 Writeln(Data[i].S);
end.


FPC 2.4.0 вообще Runtime error говорит. Тут скорее странно, что в Delphi это не воспроизводится.


 
Ega23 ©   (2013-03-27 16:25) [22]


> FPC 2.4.0 вообще Runtime error говорит.


Дык ясен пень, у тебя string с заглавной буквы прописан. :)


 
Rouse_ ©   (2013-03-27 16:28) [23]


> картман ©   (27.03.13 16:21) [20]
> зачем? Не лучше ли было сделать with только синтаксической
> конструкцией?

Видимо борьба за оптимизацию по скорости.


 
Игорь Шевченко ©   (2013-03-27 17:55) [24]


>  (а вообще лучше вообще выкинуть нафиг эту конструкцию).


Всякий овощ приносит пользу, будучи употреблен надлежащим образом в надлежащее время.


 
Rouse_ ©   (2013-03-27 19:34) [25]


> Игорь Шевченко ©   (27.03.13 17:55) [24]

Да кто-ж спорит, просто этот оператор сродни рыбе Фугу, не умеючи и отравиться можно :)


 
Игорь Шевченко ©   (2013-03-27 20:38) [26]

Rouse_ ©   (27.03.13 19:34) [25]

Неумеючи и руки порезать можно :)


 
Rouse_ ©   (2013-03-27 20:41) [27]


> Игорь Шевченко ©   (27.03.13 20:38) [26]
> Неумеючи и руки порезать можно :)

Верно подмечено :)
Надо тебе кстати как нибудь твой голубой снег в негативе припомнить - целый час на балконе промерз пытаясь повторить :)))


 
TUser ©   (2013-03-27 22:55) [28]

Дык ясен пень, у тебя string с заглавной буквы прописан. :)

Ну, шутки шутками, а имено моделей в uses ему лучше писать в том регистре, в котором файлы называются. А то под линуксом он через раз не находит, хотя и паскаль.


 
Rouse_ ©   (2013-03-27 22:57) [29]


> TUser ©   (27.03.13 22:55) [28]

Это в FPC действительно нужно?
зы: я просто не использовал - не в курсе, поэтому и спрашиваю...


 
TUser ©   (2013-03-27 23:00) [30]

Это в FPC действительно нужно?

Кажется, в документации нигде не требуется, но по факту - есть шанс получить "Unit not found" если не так. А можно и не получить, - какой-то закономерности я не нашел.


 
Rouse_ ©   (2013-03-27 23:02) [31]


> TUser ©   (27.03.13 23:00) [30]
> Кажется, в документации нигде не требуется, но по факту
> - есть шанс получить "Unit not found" если не так.

Хм... шикарно. Сеньк.


 
Rouse_ ©   (2013-03-27 23:03) [32]

А кстати - я не заметил оговорку "под линуксом", так там все регистрозависимое, вероятно отсюда и ноги.


 
Дмитрий С ©   (2013-03-28 01:00) [33]


> А кстати - я не заметил оговорку "под линуксом", так там
> все регистрозависимое, вероятно отсюда и ноги.

Это неправда. Находит модули в любом регистре безупречно. И Code Completion их список выдает нормально, в отличии от дельфи.


 
Дмитрий С ©   (2013-03-28 01:00) [34]

Ну второе  уже правда про lazarus:)


 
DVM ©   (2013-03-28 08:47) [35]

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


 
TUser ©   (2013-03-28 12:06) [36]

Это неправда.

Тебе просто повезло. Я как-то долго думал, почему не находит модуль - вот же он.



Страницы: 1 вся ветка

Текущий архив: 2013.09.01;
Скачать: CL | DM;

Наверх




Память: 0.56 MB
Время: 0.007 c
15-1363267161
brother
2013-03-14 17:19
2013.09.01
У кого Win7x64


15-1364323263
TStas
2013-03-26 22:41
2013.09.01
Как узнать, как войти в БИОС ноутбука?


2-1356154304
Виталий
2012-12-22 09:31
2013.09.01
как оцифровать звук (.wav)


15-1364240820
Дмитрий С
2013-03-25 23:47
2013.09.01
Нет ли в дельфи каких-нибудь волшебных констант?


15-1364325103
TStas
2013-03-26 23:11
2013.09.01
А написал ли сам MicroSoft что-то на C#?