zHz00 Untitled

пятница, 19 августа 2016
23:21 На долгую память о старых друзьях
Я отлаживал программу. Программа работала на компе с подключённым прибором. Возможности поставить туда среду разработки или удалённый отладчик не было.

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

Спустя 4 дня отладки я обнаружил наконец, в чём заключалась ошибка в коде. Вот отрывок. Зависание иногда происходило где-то в нём, причём один раз на 10-20 выполнений.

bFlag=GetStatus();
fTime=0;
while(!bFlag&&fTime<0.5);
{
//do something
fTime=TimeElapsed();
bFlag=GetStatus();
}


Вписать в само условие GetStatus() по некоторым причинам было нельзя (я привожу тут текст упрощённо). Функция TimeElapsed() возвращает, сколько прошло времени с начала ожидания. Если прошло 0.5 секунды, дальше мы начинаем сушить вёсла, продолжение нормальной работы невозможно. Функция GetStatus() проверяет состояние другого потока в многопоточном приложении -- не возникло ли в нём определённое событие? Если статус равен фолс (не возникло), ждём дальше. Если тру (возникло) -- выходим. Специфика её работы такова, что вернувши один раз тру, дальше она начинает возвращать фолс -- до следующего возникновения события во втором потоке. Я долго грешил на ошибки синхронизации, тыкал палочкой в критические секции -- безрезультатно. Пока не обнаружил при помощи отладочной печати странную вещь. bFlag до цикла равно тру, а внутри цикла -- фолс. В тех случаях, когда программа не зависала. Это значит, что ГетСтатус вызывался два раза. Это невозможно, т.к. Если он равен тру, в цикл вход происходить был не должен -- условие сразу ложно! И тут я присмотрелся повнимательнее...

***


Когда я ещё не имел опыта программирования на Си, классе так в восьмом-девятом, я всё же написал одну довольно об'ёмную по тем меркам программу. Но она не работала. Это была компьютерная игра. Основной цикл был прост, как число 3. Прорисовать экран -- дать пользователю сделать ход -- если конец игры достигнут, сделать выход. И с начала. Проблема была в следующем месте:

1	while(1);
2 {
3 Draw();
4 GameMove();
5 if(GameOver())
6 break;
7 }


Это плохой стиль. Не рекомендуется делать бесконечный цикл в тех условиях, когда чётко понятно условие выхода. Это допустимо делать, когда этих условий много и они сложно комбинируются.

При отладке наблюдалась странная ситуация -- после строчки 1 программа зависала. Я нажимал "следующую строку", но она продолжала висеть на этой. Кроме всего прочего выдавался варнинг -- unreachable code на строках 3-6. Это было очень странно. Я же всё чётко написал! Строчка должна выполняться. Я искал ошибку полгода, с перерывами. Потом до меня допёрло -- в первой строке в конце стояла точка с запятой. Это и было тело цикла. Т.е. это был бесконечный пустой цикл. И он бесконечно выполнялся, не переходя к строчке 3.

Я каждый год предупреждаю об этом всех студентов. И сам, спустя много лет, попал в ту же ловушку. Но попадание туда было закономерным -- дальше по тексту был похожий цикл, но с постусловием. Очень похожим. Чтобы не писать два раза (по факту оно было несколько сложнее, чем я привожу в примере), я его просто скопировал. А в цикле с постусловием точка с запятой быть как раз должна.

Вообще первый отрывок написан очень плохо. Его надо было тоже делать с постусловием.

@темы: Программирование, Говнокод, Борьба с техникой

URL
Сейчас поменяю все стандартные мастдайские иконки! А то к...
Пошла седня на "Люди-мыши"... ни тех, ни других...
"Прелесть, а не картинка. Крики, угрозы, слезы. Заме...
Кто знает нормальные маршруты в горы, чтоб людей (местых ...
Не могу не процитировать - настолько понравилось. «О...
Подруга не звонила месяц. А я забыла поздравить ее с днем...

20.08.2016 в 01:49

20.08.2016 в 01:49
Хм, у тебя тоже пост о старых друзьях! Это специально или случайность?
URL

20.08.2016 в 01:54

20.08.2016 в 01:54
Случайность. Под старым другом я имел ввиду данную ошибку, которую впервые совершил давным-давно.
URL

20.08.2016 в 08:16

20.08.2016 в 08:16
Старый друг лучше молод новых двух.
URL

20.08.2016 в 10:52

20.08.2016 в 10:52
А я как прочел - подумал, что у си так и надо по синтаксису хд
URL

21.08.2016 в 08:52

21.08.2016 в 08:52
Пока читал пост, тоже точку с запятой в твоём тексте прошляпил. Глаз привык от описания цикла соскальзывать вниз, на, так сказать, его прелести.
URL

22.08.2016 в 09:49

22.08.2016 в 09:49
потому я пишу скобки всегда и использую египетские скобки, вместо обычных.
URL

22.08.2016 в 12:06

22.08.2016 в 12:06
Е-египетские?

Мне уже двое сказали что для защиты от этого надо использовать бсд-скобки, т.е. когда открывающая на той же строке что и заголовок.
URL

22.08.2016 в 12:37

22.08.2016 в 12:37
египетские скобки
Арабский код, ЛоЛ.
Вообще-то, строго говоря, они древнеегипетские, но т.к. разговорный вариант, то "древне-" опускается.

URL

22.08.2016 в 13:25

22.08.2016 в 13:25
о, кто-то их зовёт BSD.
я из этой кажется статьи узнал про habrahabr.ru/post/96978/
URL
Добавить комментарий

Расширенная форма

Подписаться на новые комментарии
Получать уведомления о новых комментариях на E-mail