Форум: "Начинающим";
Текущий архив: 2009.06.28;
Скачать: [xml.tar.bz2];
ВнизАбсолютно необъяснимый глюк Найти похожие ветки
← →
Franzy (2009-04-30 15:05) [0]Форма. На форме кнопка. По нажатию на кнопку производится обработка заранее подготовленных данных. Нажимаем первый раз - результат правильный. Нажимаем второй раз - результат правильный, причем точно такой же, как в первый раз. Нажимаем третий (четвертый, пятый...) раз - результат неправильный, и обратно каждый раз одинаковый!
Каждый раз входные данные одни и те же. В обработчике кнопки они никак не модифицируются. Только на их основе образуются новые данные. Эти новые данные каждый раз все честно обнуляются. В том числе всем дин. массивам присваиваю := nil.
Как такое может быть?!!!! Я в полном ступоре. Я понимаю, если бы каждый раз результат был разный или всегда одинаковый неправильный. Но тут при первом и втором нажатии результат А, а при всех последующих - результат Б. Ерунда какая-то!
Код очень объемный, поэтому не привожу. З.Ы. Нажатия кнопки в программе тоже никак не считаю. Все переменные, кроме временных и счетчиков, глобальные.
← →
sniknik © (2009-04-30 15:18) [1]> Код очень объемный, поэтому не привожу.
настолько большой, что даже файлообменник не принимает? ну попробуй выдели только относящееся к глюку в пример, может хоть он будет меньше 150мгбайт (стандартное ограничение файлообменников).
или ты так просто ради сочувствия на форуме спрашиваешь? это вообще то работа психиатров, сочувствовать.
← →
Anatoly Podgoretsky © (2009-04-30 15:27) [2]> Franzy (30.04.2009 15:05:00) [0]
> Все переменные, кроме временных и счетчиков, глобальные.
← →
Плохиш © (2009-04-30 15:48) [3]
> Franzy (30.04.09 15:05)
>
>
Ошибка в 503456 и 765493 строках.
← →
Franzy (2009-04-30 16:12) [4]2Плохиш
Нет, не угадали. В строке номер 136701! :)
Как выяснилось, в одном месте я обращался к несуществующему элементу дин. массива. Соотв.-но, мне возвращалось случайное число. Любопытно, однако, что первые два раза это число было правильным (нулем), а последующие разы - другим числом :) Магия.
Как исправил, каждый раз теперь что нужно возвращает...
← →
Leonid Troyanovsky © (2009-04-30 17:09) [5]
> Franzy (30.04.09 16:12) [4]
> :) Магия.
Свезло.
Могло б, скажем, на 2009 разу.
--
Regards, LVT.
← →
@!!ex © (2009-04-30 18:58) [6]> [4] Franzy (30.04.09 16:12)
> Любопытно, однако, что первые два раза это число было правильным
> (нулем), а последующие разы - другим числом :) Магия.
Не знаю как работает - значит магия. :)
И не такую магию видели....
С указателями надо быть ОЧЕНЬ аккуратным. Таких ошибок можно наполучать - век не разгребешь.
← →
Юрий Зотов © (2009-05-01 00:04) [7]> Franzy (30.04.09 16:12) [4]
Случайное поведение программы, очевидно, может быть вызвано только ее обращением к случайным данным. Это обращение и надо искать.
Какой же это "абсолютно необъяснимый глюк"? Вполне объяснимый, притом еще и однозначно.
← →
Германн © (2009-05-01 00:11) [8]С автором такое уже не впервые :)
http://delphimaster.net/view/2-1240482954/
← →
Юрий Зотов © (2009-05-01 01:44) [9]Недавно наткнулся на такой пример "абсолютно необъяснимого глюка".
Есть объект O1. До какого-то места в программе он содержит правильные данные, а затем эти данные непостижимым образом портятся. И вот что в итоге выяснилось:
O1 := O2;
// Объект O2 был предварительно заполнен правильными данными, поэтому
// теперь объект О1 тоже содержит правильные данные.
// Далее идет много-много недетского кода, много разных вызовов.
// И, наконец:
O2.F1 := ...;
O2.F2 := ...;
// Вуаля! Объект O1 неявно испорчен!
Абсолютно непостижимый глюк. Но не программы, а программиста.
← →
Кролик-Фролик (2009-05-01 01:57) [10]так автор программирует по-современному - надергал кода из разных мест, слепил нечто
если появляются ошибки, то строки кода удаляются, пока ошибка не исчезнет
этот стиль щас самый модный
← →
AndreyV © (2009-05-01 02:15) [11]> [9] Юрий Зотов © (01.05.09 01:44)
> O1 := O2;
> // Объект O2 был предварительно заполнен правильными данными, поэтому
> // теперь объект О1 тоже содержит правильные данные.
Получается, что неправильные.
← →
Германн © (2009-05-01 02:16) [12]
> Юрий Зотов © (01.05.09 01:44) [9]
>
> Недавно наткнулся на такой пример "абсолютно необъяснимого
> глюка".
>
Это уже "перебор" :)
Ты уж, Юра, извини! Мы говорим о вопросе "этого автора"?
Но и твой пример в Юрий Зотов © (01.05.09 01:44) [9]- не впечатляет, увы.
← →
KSergey © (2009-05-01 15:42) [13]> Franzy (30.04.09 16:12) [4]
> Как выяснилось, в одном месте я обращался к несуществующему элементу дин. массива.
Ставим галку check range в свойствах проекта - и избавляемся от подобных ошибок однозначно. Пусть и некоторой потерей скорости.
Потом после тестов в релизном варианте галку снять и пересобрать.
← →
Юрий Зотов © (2009-05-01 15:48) [14]> Германн © (01.05.09 02:16) [12]
> Но и твой пример не впечатляет, увы
Меня впечатлил, и даже очень. После того, как полдня эту плюху вылавливал, да еще и без отладчика (были на то причины).
> KSergey © (01.05.09 15:42) [13]
Вот по этой причине она у меня всегда и включена.
← →
Sapersky (2009-05-01 16:57) [15]Ставим галку check range в свойствах проекта
Не-а, не работает. Видимо, это только для статических массивов, защита от переполнения буфера фиксированного размера.
← →
Anatoly Podgoretsky © (2009-05-01 20:52) [16]> KSergey (01.05.2009 15:42:13) [13]
Потом, после тестов не снимаем.
← →
Германн © (2009-05-01 21:21) [17]
> Anatoly Podgoretsky © (01.05.09 20:52) [16]
>
> > KSergey (01.05.2009 15:42:13) [13]
>
> Потом, после тестов не снимаем.
+1
← →
@!!ex © (2009-05-01 21:26) [18]> [16] Anatoly Podgoretsky © (01.05.09 20:52)
А как же быть с объявлениями типа:type
TIntArray = array[0..0] of integer;
PIntArray = ^TIntArray ;
var
A:PIntArray ;
begin
A:=GetMem(sizeof(integer)*1000);
end;
Вполне законное объявление динамического массива. Но насколько я помню - Range Cjeck не проходит.
← →
sniknik © (2009-05-01 21:35) [19]> Вполне законное объявление динамического массива.
вообще то нет, это не динамический массив, это способ каким обходились до того как появились динамические массивы. и насколько помню Range Cjeck для него надо было выключать.
либо "обманывать" типаtype
TIntArray = array[0..32767] of integer;
PIntArray = ^TIntArray ;
var
A:PIntArray ;
begin
A:=GetMem(sizeof(integer)*1000);
← →
@!!ex © (2009-05-01 21:58) [20]> [19] sniknik © (01.05.09 21:35)
> вообще то нет, это не динамический массив
И чем же это НЕ динамический массив? :)
Это не тот динамический массив, что сейчас используется(в основном), но это вполне себе динамический массив.
← →
Anatoly Podgoretsky © (2009-05-01 23:48) [21]> @!!ex (01.05.2009 21:26:18) [18]
Так делать не надо!
← →
@!!ex © (2009-05-02 08:25) [22]> [21] Anatoly Podgoretsky © (01.05.09 23:48)
Почему?
← →
Anatoly Podgoretsky © (2009-05-02 09:01) [23]Тип объявлен на один элемент, и чтобы работать надо выключить проверку диапазонов. А нужды делать на один элемент нет никакой. Эта техника была взята из СИ, где массивов как таковых и не было, и не было проверки диапазонов.
← →
Юрий Зотов © (2009-05-02 09:38) [24]> @!!ex ©
А если объявить так:
type
TIntArray = array[0..с_запасом] of integer;
то проверку выключать не придется.
← →
@!!ex © (2009-05-02 09:48) [25]> [23] Anatoly Podgoretsky © (02.05.09 09:01)
> [24] Юрий Зотов © (02.05.09 09:38)
Согласен.
← →
sniknik © (2009-05-02 10:57) [26]> Согласен.
а со мной значит нет, когда я говорил про "обман" и подставил число гарантированно больше твоей 1000.
не те слова сказал? у тебя есть "табу" на восприятие/понимание отдельных слов?
> то проверку выключать не придется.
но для конкретно вот этого случая она будет мало что значить... т.к. при выделении памяти под 1000 никак не отреагирует на влезание в "запас" с 2000е поле например. что грозит теми же проблемами что у автора.
надо просто использовать динамические массивы и все, не выделываясь.
← →
Юрий Зотов © (2009-05-02 11:11) [27]> sniknik © (02.05.09 10:57) [26]
Не возражаю, чес-слово.
:о)
← →
@!!ex © (2009-05-02 11:49) [28]> [26] sniknik © (02.05.09 10:57)
> а со мной значит нет, когда я говорил про "обман" и подставил
> число гарантированно больше твоей 1000.
> не те слова сказал? у тебя есть "табу" на восприятие/понимание
> отдельных слов?
С чего вы взяли что "нет"? Я никак не комментировал, только спросил почему этот вариант - не динамический массив?
← →
Sapersky (2009-05-02 12:11) [29]надо просто использовать динамические массивы и все, не выделываясь.
Гхм. Всё-таки, кто-нибудь реально проверял, работает Range checking с дин. массивами или нет?
У меня (D5) - не работает. И в хелпе написано "Long strings are not range checked", а дин. массивы по сути те же строки.
В общем, ИМХО, довольно бестолковая вещь этот Range checking, если в основном приходится думать, как его обойти...
Что касается "не выделываясь" - массивы-указатели (как я их называю) удобны тем, что к ним можно откастить изначально нетипизированный указатель (многие API возвращают данные в таком виде), и таким образом получить удобный доступ к данным.
Хотя можно откастить и к дин. массиву... если потом избегать обращения к его длине... и вручную прибить его, чтобы Delphi не пытался... но это уже хакерский выпендрёж :)
← →
Юрий Зотов © (2009-05-02 12:48) [30]
type
TIntArray = array of integer;
procedure TForm1.FormCreate(Sender: TObject);
var
A: TIntArray;
begin
SetLength(A, 10);
Caption := IntToStr(A[11]);
end;
В D7 Получаем ERangeError с сообщением "Range check error".
← →
Юрий Зотов © (2009-05-02 12:59) [31]> Sapersky (02.05.09 12:11) [29]
> довольно бестолковая вещь этот Range checking, если в основном
> приходится думать, как его обойти...
А его и не надо обходить. Надо включить - и спокойно писать код, не заморачиваясь на эту тему. И если вдруг получим ERangeError, то будем обязаны сказать "Спасибо" за такой шикарный сервис. Потому что без него запросто могли бы нарваться на завуалированный глюк и до-о-олго искать, что это вдруг программа работает не так, как надо.
Пример - сабж. Лучше не придумаешь.
← →
sniknik © (2009-05-02 14:37) [32]> только спросил почему этот вариант - не динамический массив?
дальтоник? зеленый огонек от оранжевого отличить не можешь?
одно тип, другое указатель. пусть этот указатель и типизированный. причем если бы там был указатель на четко ограниченный размерностью массив, то Range checking тоже бы нормально срабатывал, но в "твоем" случае с [0..0] его приходится отключать т.к. заведомо вылазим за границы типа, в "моем" с [0..32767] обманывать, т.к. заведомо определяем больший размер чем выделим, и Range checking не знает о реально выделенном размере. (ну мы же специально его обманули)
а настоящий динамический массив включает в себя и длину, т.что есть по чему определять границу выхода и генерировать исключение.
это все так очевидно, не знаю даже зачем объяснять...
все одно что отвечать на вопрос почему 2 сваренных за рамы мотоцикла не являются машиной, ну, у них же тоже в сумме 4 колеса получилось...
← →
Amoeba © (2009-05-02 15:33) [33]
> в "моем" с [0..32767] обманывать, т.к. заведомо определяем
> больший размер чем выделим, и Range checking не знает о
> реально выделенном размере
Так Borland сделал например, в TList.
← →
Sapersky (2009-05-02 15:33) [34]Юрий Зотов © (02.05.09 12:48) [30]
Да, как выяснилось, иногда работает...
У компилятора какая-то странная логика с вставкой этой проверки. Пытается оптимизировать, что ли, и выкидывает проверку если результат не используется (хотя даже в этом случае теоретически можно получить AV):
SetLength(arr, 3); Sum := 0;
For n:=0 to 3 do Inc(Sum, arr[n]); // проверки нет
А если добавить в конец Caption := InttoStr(Sum); - проверка работает.
Но самая неприятная, гм, "особенность" связана с массивами записей. Сейчас потестировал и выяснил, что Range Check работает только при размере записи <= 8 байт (???). Хотя, казалось бы, какая ему разница, длина-то массива лежит всегда в одном месте независимо от размера записи... и она всегда в элементах, не в байтах...
Недавно как раз "нарвался" именно с массивом записей. С почти такими же спецэффектами как у автора вопроса. Range Check радостно "завалил" программу на массиве-указателе вроде [18], а на реальном глюке молчал как партизан.
← →
sniknik © (2009-05-02 15:50) [35]> Так Borland сделал например, в TList.
да, и они наверняка проверяют сами, сколько там памяти под него реально выделено.
← →
sniknik © (2009-05-02 15:56) [36]> // проверки нет
тут оптимизатор скорее всего выкинул вообще весь код, не только проверку, т.к. результат не используется.
> а на реальном глюке молчал как партизан.
а ты тоже не молчи как партизан, распространяя только дезу, приводи реальный код при глюке, чтобы можно было проверить. или указать истинного виновника.
← →
Sapersky (2009-05-02 16:15) [37]тут оптимизатор скорее всего выкинул вообще весь код
Нет, не выкинул.
приводи реальный код при глюке, чтобы можно было проверить. или указать истинного виновника.
Я уже разобрался там. Меня больше интересует работоспособность Range Check в расчёте на будущее, чтобы не искать глюки долго и мучительно.
А пример - да взять хоть [30] и переделать под записи.
type
TMyRec = record
i1, i2 : Integer;
i3 : Integer;
end;
TMyRecArray = array of TMyRec;
procedure TForm1.FormCreate(Sender: TObject);
var
A: TMyRecArray;
begin
SetLength(A, 10);
Caption := IntToStr(A[11].i1);
end;
Потом закомментируйте i3...
← →
sniknik © (2009-05-02 17:37) [38]> Потом закомментируйте i3...
а вот это действительно странно, т.е. наоборот странно пока не заментаришь... спасибо за предупреждение.
← →
Юрий Зотов © (2009-05-02 21:54) [39]Братцы! Программеры! Я вас люблю!
За то, что вы такие.
Не шутка.
:o)
← →
Юрий Зотов © (2009-05-02 21:59) [40]Еще в тему.
Всегда преклонялся (не шучу) перед разработчиками компиляторов.
Эта ветка - тому подтверждение.
Спасибо.
← →
Palladin © (2009-05-03 15:39) [41]"А еще удивляются, почему я в церковь не хожу" (С) Догма
← →
Cobalt © (2009-05-07 22:07) [42]Хмм, только что проверил на Д2007 - никакого эффекта
← →
Sapersky (2009-05-08 16:40) [43]Проверил на разных версиях.
В D5, D7, и 2006 эффект есть. В 2009 - нет, т.е. всё нормально, вываливается с ошибкой независимо от размера записи.
В 2007-м, значит, заткнули дырку. Не прошло и 10 лет...
Страницы: 1 2 вся ветка
Форум: "Начинающим";
Текущий архив: 2009.06.28;
Скачать: [xml.tar.bz2];
Память: 0.57 MB
Время: 0.006 c