Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Прочее";
Текущий архив: 2013.09.01;
Скачать: [xml.tar.bz2];

Вниз

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

 
Дмитрий С ©   (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;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.54 MB
Время: 0.003 c
15-1364300349
MBo
2013-03-26 16:19
2013.09.01
Касперский не любит мою программу


15-1364278811
N.Cage
2013-03-26 10:20
2013.09.01
Amd vs Intel


15-1364473732
alexdn
2013-03-28 16:28
2013.09.01
Переносной принтер


15-1354481915
Конь Як
2012-12-03 00:58
2013.09.01
Веб программирование


2-1355836063
oam333
2012-12-18 17:07
2013.09.01
Как через TMemoryStream и IdTCPServer передать переменную Record





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский