• ↓
  • ↑
  • ⇑
 
Записи с темой: программирование (список заголовков)
21:45 

Разгрузочный день

Рекламирую то, что сегодня в гугло-дудлах:

g.co/doodle/jr54y7?ds=cl

Challenge: по крайней мере на 4 и 6 уровнях можно сделать на шаг короче чем то, что заявлено минимальным.

@темы: Программирование, Игры

23:25 

Указатели и адресное пространство for dummies

Понимание сути указателя не может быть "поверхностным" или "глубоким". Либо оно есть, либо его нет.

(читать дальше)

@темы: Программирование, Статьи, Студенты

23:59 

Близнецы

Однажды я ехал в метро на лекцию (читать). Лекцию по Си. Тогда я ещё недостаточно хорошо ориентировался в материале, поэтому в метро я его повторял. Часть материала данной лекции я собирался излагать по книге авторов Фомин и Подбельский (очень её я люблю). В узких кругах книга известная. Она у меня была с собой. Бумажная.

Сижу, читаю. Рядом сидит паренёк и постоянно ко мне в книгу заглядывает. Что ему, блин, нужно?

Я продолжаю изучать книгу.

Доехал до целевой станции, встаю у дверей, а сам на паренька решил посмотреть.

А у него в руках пакет полиэтиленовый. А сквозь него такая же книга просвечивает.

@темы: Программирование, Книги

23:44 

Строить не ломать

Итак, у меня было две версии функции. Одна была отлично откомментирована и хорошо разбита на блоки. Но она не работала. Это было следствие рефакторинга.

Другая работала. Изначальная. Но выглядела мягко говоря... не очень.

Передо мной стояла задача: надо было заставить первую работать.

Было две стратегии:
1. Долгим вдумчивым взглядом пытаться понять, чем же эти функции различаются, и где я облажался;
2. По частям вводить отрефакторенные блоки в старую функцию и следить, когда она перестанет работать.

Второе оказалось эффективнее.

@темы: Программирование, Очевидное-невероятное, Говнокод

23:58 

Ключевое слово static в C-like языках: масонская ложа

На эту тему существует несколько заметок, однако я счёл, что все они обладают недостатками. Поэтому решил написать свою.

Ключевое слово static -- наверное, самое эзотерическое слово в языках Си-группы. Во-первых, оно довольно редко нужно (но volatile, конечно, круче). Во-вторых, в зависимости от контекста это слово может означать довольно разные вещи.

(читать дальше)

@темы: Программирование, Статьи

23:59 

Знаковые числа как беззнаковые

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

(осторожно, шестнадцатеричные представления чисел!)

@темы: Программирование, Говнокод

23:59 

О MATLAB

А MATLAB я нежно люблю. Но сначала про Excel.

Раньше я не понимал, для чего нужны табличные процессоры типа Excel. Ну таблицы, и чего. В ворде тоже можно нарисовать! Но потом я узнал, что в Excel есть формулы! И тогдя стало понятно одно из главных назначений экселя. Эксель -- это огромный калькулятор. У него куча возможностей. Есть существенный недостаток -- названия всех функциях в формулах локализованы, поэтому бессмысленно читать справку на любом языке отличном от языка интерфейса. СРЗНАЧ(...) написано только в русском руководстве.

MATLAB свободен от данного недостатка. Кроме того, он ещё более мощный калькулятор. Если вам надо быстро что-то сделать с большим массивом данных, то при наличии минимальных навыков то же самое гораздо эффективнее делается в нём, а не в экселе. Но следует готовиться к тому, что всё придётся писать в командной строке.

С Матлабом я познакомился очень давно, ещё в виде его DOS-версии. Я ещё в школе учился. У меня было отксерокопированное (!) руководство, которое папа принёс с работы. Тогда мне не надо было ничего обрабатывать. Я просто игрался. demo -- показать демонстрации, в том числе трёхмерные графики (под DOS!). ans -- показать результат последней команды. who -- показать, какие переменные существуют в текущий момент. plot -- нарисовать график.

Эти команды работали в Матбале 30 лет назад. Работают и сейчас.

Когда мне в 8 классе подарили програмируемый калькулятор МК-85, я стал его изучать. Там тоже всё решалось командной строкой. И я стал забивать туда команды Матлаба. Просто так. Естественно, ни одна команда не работала. Хотя постойте!

Я написал "WHO". И на экране появилась надпись:

"Программу разработал Подоров А.Н."

Так я впервые в жизни самостоятельно обнаружил пасхалку.

@темы: Случай из жизни, Программы, Программирование

23:57 

Об ошибках

Часто говорят, что умный тот, кто учится на ошибках (своих, чужих), но подробно данное утверждение не раскрывается.

Рассмотрим для примера сферу, которая мне знакома, и где большое поле для совершения ошибок.

Программирование.

Ошибки-то всё равно совершаются, хотя и реже. Но есть ещё один момент.

Скорость обнаружения. Если сделал ошибку однажды -- и потом с великим трудом её вычислил и осознал -- то в следующие разы уже малейший симптом вызывает ощущение "так, где-то я это уже видел". А потом вспоминаешь, что это означает. Так можно пропустить большой кусок работы и перейти к следующим делам.

Естественно, то же касается и ошибок, совершённых другими. Их опознание также происходит очень быстро.

@темы: Мысли, Восприятие, Программирование

23:59 

Не индукция

Однажды я и представитель дружественной организации налаживали обмен данными между нашими устройствами. Я с нашей стороны, а он -- с их стороны.

И связь была... нестабильная. Данные иногда считывались с ошибками. Мы находили проблемы в своих программах и исправляли их. Но результат всё равно был плохой.

И вот, вроде все проблемы более-менее решили. И я говорю: Ладно. Если 10 раз всё отработает без ошибок, будем считать, что работает.

А второй товарищ отвечает: А давай так. Если на 11-й раз произойдёт ошибка -- я тебя застрелю. Согласен?

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

А ошибка возникла гораздо раньше 11-го раза.

@темы: Мысли, Программирование

23:59 

In God We Struct

Написано:

struct
{
//...
} A,B,C;


Это должно об'являть три переменных, типом которых является (безымянный) тип этой структуры.

Однако компилятор сообщает, что вместо A,B,C он ожидает идентификаторы. Но ведь это и есть идентификаторы!

Проверяю на кириллицу -- не подходит, буквы латинские. Заменяю на A0,B0,C0 -- всё работает.

Странно.

ОКАЗАЛОСЬ

что в заголовочном файле уже были строки:

#define A 0
#define B 1
#define C 2

В итоге последняя строка определения переменных после подстановок выглядела так:

} 0,1,2;

Да, тут идентификаторов действительно нет.

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

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

22:32 

Untitled [492]

Коллега, которому передали один из проектов, с которым раньше работал я, поставил себе PVS Studio. Статический анализатор кода. И он стал выявлять разные шняги, которые написал, как выяснилось, я (и это подтверждает SVN Blame).

Вот один из таких кусков, высранных мной в 2013 году.

int nx, ny, nresx, nresy;
char file_list[80][261];
unsigned stx[80], sty[80];
fscanf(f, "%d %d %d %d\n", &nx, &ny, &nresx, &nresy);
int nitems, nfiles;
for(nfiles = 0, nitems = 0; !feof(f); nitems++, (strcmp(file_list[nfiles], "-") &&
(file_list[nfiles][0] != '\0')) ? nfiles++ : 0/*иначе ничего*/)
{
fgets(file_list[nfiles], 261, f), file_list[nfiles][strlen(file_list[nfiles]) - 1] = '\0';
if(strcmp(file_list[nfiles], "-"))
{
fscanf(f, "%d %d\n", &stx[nfiles], &sty[nfiles]);
}
}

//nitems--;// |---+----\_______. КОСТЫЛЬ
//nfiles--;// |---+----/ КОСТЫЛЬ


Что тут происходит конкретно, я уже и сам толком не помню. Предлагаю попробовать догадаться самостоятельно. f -- текстовый файл, откуда что-то считывается.

Интересно тут следующее:
1. Заголовок цикла for содержит не только операцию "запятая" в инициализаторе, но и в повторяемом выражении (третья часть).
2. Первый оператор цикла также содержит в себе операцию "запятая".
3. Комментарий с костылём смешной. Но почему-то закомментирован.
4. PVS Studio ругался не на стиль, а на то, что я условное выражение в операции ?: не обернул в скобки (хотя функционал от этого не меняется, т.к. у && приоритет выше, чем у ?:).

Зачем же были нужны операции запятая в таком количестве? Она тут только ухудшает читаемость. У меня только одна гипотеза. Изначально в теле цикла был только первый оператор. Когда ситуация усложнилась, я не захотел вводить новый блок, поэтому использовал всевозможные извращения, ТОЛЬКО БЫ обойтись одним оператором в теле цикла. Но в итоге сдался. А то, что уже запихнул в заголовок и в операцию "запятая", доставать обратно не стал. Эх...

@темы: Программирование, Говнокод

23:58 

Как я позвонил себе на жёсткий диск

Не буду вдаваться в технические детали. Скажу коротко -- прежде, чем записать во флеш-память внутри микроконтроллера, её надо стереть.

В контроллерах STM32 для этого есть специальная операция. Надо в специальный регистр записать не менее специальное значение. И ещё -- номер сектора для стирания. Секторов штук 10-15 (в разных моделях по разному).

Я стирал два сектора -- номер 10 и номер 11. По очереди.

Управляющий регистр 32-битный, там ещё куча других настроек. Трогать я их не хотел. Поэтому я воспользовался побитовым "или":

FLASH->CR|=sector<<3;//установить номер сектора: биты 3-6.

И тут я заметил, что когда я стираю сектор 10, всё хорошо. Сектор 11 -- всё хорошо. Но когда я после этого опять стираю 10 -- происходит ошибка стирания. Нет, все статусные регистры хороши, но когда я де-факто смотрю в содержимое памяти, оказывается, что она не стирается! Долго прохожу с отладчиком, грешу на недостаточное напряжение питания, на закончившиеся циклы перезаписи (хотя 10000 израсходовать не так просто).

Потом начинаю уже параноидально проверять содержимое всех управляющих и статусных регистров флеш-модуля после каждой строчки. И обнаруживаю, что стирание происходит у сектора 11, а не 10! То есть, я всё время стирал не тот сектор?!

Т.е. я передаю в регистр сектор 10, а в регистре указано:

ХХХХХХХХ ХХХХХХХ ХХХХХХХХ Х0011ХХХ

(как вы догадались, номера секторов выше были в двоичной системе счисления)

Тут я понял, что виной всему было побитовое логическое или. 1|0 будет 1. То есть после записи сектора 11 (3) в регистр, запись 10 (2) поверх него давала опять 11 (3).

Т.е. мне надо было не просто НАБИТЬ ЕДИНИЦАМИ нужные поля, а набить конкретными значениями, включая нули. Побитовое "или" тут уже не подходит. В итоге я решил проблему так:

FLASH->CR=(FLASH->CR&0xFFFFFF00)|(sector<<3);//ценные биты были только в старших трёх байтах

А ведь в ассемблере для этого есть специальная команда -- BFI.

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

23:56 

Докажи, что ты не верблюд

Есть два измерительных модуля в одном из приборов, которые мы делаем -- Module1 и Module2. Меряют разные характеристики. Один давление, другой температуру.

Разные экземпляры прибора могут быть укомплектованы разным набором модулей. И вот данный конкретный экземпляр имел только Module2. И его эксплуатировали 4 месяца не включая этот модуль. Но понадобилось включить. И оказалось, что модуль не работает.

Начинаю отлаживать ПО. И вижу картину маслом:

void InitModule2()
{
if(Settings.bModule1Installed==false)
return;
//далее инициализация модуля2
}


То есть: если в приборе НЕ установлен модуль 1 (а он и не был установлен), прекратить инициализацию модуля 2! Модули более-менее независимы. Это меня удивило. Я сделал svn blame. Эта команда позволяет установить, кто является автором каждой строчки кода (и у неё есть синоним -- svn praise, лол). В результате этого я выяснил, что данную строчку написал начальник. Как раз 4 месяца назад. И тогда же обновляли ПО на данном приборе.

Я подумал, может быть в этом есть глубокий смысл? Спросил его. Но оказалось, что это всего-навсего последствия невнимательного копипаста при рефакторинге. В первом модуле текст был правильным. Инициализация отличалась несильно. Поэтому начальник просто скопировал текст инициализации первого модуля и подправил нужные места. Но не все...

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

23:59 

Адресная книга

Обнаружил у себя в закромах родины собственноручно написанный эмулятор калькулятора МК-52 под калькулятор же МК-85. В виде листинга на пяти листах. Написанного от руки.

И вспомнил, как я его писал. Ещё в школе.

МК-52 -- программируемый советский микрокалькулятор. Он программируется в шестнадцатеричных кодах. Для их ввода можно воспользоваться клавиатурой, на которой написаны разные действия. Это помогает вводить программу и записывать на бумагу. На бумаге пишут, к примеру:

1 2 В↑ + С/П

Это значит, сложить 1+2 и закончить программу.

А в памяти (и встроенном просмотрщике программы) это отображается так:

50 10 0E 02 01 (читать надо справа налево)

МК-85 более крутой, он поддерживает Бейсик. Писать эмулятор на Бейсике извращение ещё то. Нет, место, где хранить программу в кодах -- есть. Вопрос, как её выполнять?

Допустим, в переменной D хранится код очередной операции. И её надо выполнить. Что же делать?

Логика подсказывает простой дедовский способ:

100 IF D=0 THEN ...
101 IF D=1 THEN ...

И тут меня ждала засада. В двух частях:
1. В МК-85М всего 5317 байт (!) памяти. Да, бейсик представляется в байт-коде, т.е. каждое ключевое слово идёт как 1 байт. Но всё равно памяти мало.
2. Скорость выполнения программ будет просто умопомрачительной, т.к. чтобы выбрать правильное действие, надо пробежать в среднем по половине проверок. А работает калькулятор так, что добавка даже 2-3 строчек, выполняющих простые операции -- заметна.

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

Потом я прочитал интересную вещь. Оказывается в Бейсике МК-85 команды GOTO и GOSUB (подпрограмма) могли принимать не только число. Но и переменную. И даже выражение.

Это и был ключ к тому, как всё уместить в 5317 байт.

100 GOSUB D+1000
//...
1000 (действие1):RETURN
1001 (действие2):RETURN
//и так далее для всех ~200 кодов операций

@темы: Программирование, Говнокод

23:58 

Коллекционер стеклотары

Появилась необходимость сохранять настройки в энергонезависимой памяти микроконтроллера. МК STM32. У большинства моделей отсутствует EEPROM, специально для этого предназначенный. Поэтому остаётся только одно -- хранить в основной флеш-памяти, где прошивка.

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

Забиваю в гугл "работа с флеш-памятью stm32". Передо мной появляется 10+ статей (и я наверное напишу ещё одну). Во всех написано примерно одно и то же. Но интересовал меня изначально в статьях строго определённый момент: как определить, где заканчивается прошивка, чтобы использовать под свои данные свободное место?

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

Потом я нашёл одну (!) статью, где интересующий вопрос освещался не с позиции рандома.

@темы: Программирование, Говнокод

23:58 

Парадокс близнецов

Отлаживал программу при помощи отладочных сообщений. То есть, сообщения по ходу выполнения программы пишутся в лог-файл.

Время на часах -- 14:00. Выполняю в программе операцию, которую хотел проверить. Захожу в лог -- а там последняя запись -- 13:52. Что такое?! Как так?! Проделал ещё раз операцию -- то же.

Потом меня стали терзать смутные сомнения. Я отодвинул окошко, загораживающее часы в трее.

13:53.

Работал на целевом компьютере я по удалёнке. И смотрел время по часам основного компа. Часы целевого компа отставали на 8 минут.

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

23:59 

То лапы ломит, то хвост отваливается

Перевели один из проектов из-под MSVS 2008 под MSVS 2015. Экзешник вырос в два раза и перестал работать под Windows XP! (наше ПО эксплуатируется в т.ч. на машинах с XP)

Потом, правда, оказалось, что есть специальный набор legacy-библиотек.

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

23:58 

Лакуна

Смотрю, пакеты от устройства приходят не целиком (это как раз то, из-за чего я делал вчерашние дампы). Конкретно -- отсутствует начало. А иногда вообще ответ не приходит. Причём "недостача" возникает только в начале процесса обмена. Если первый пакет пришёл нормально, все остальные тоже приходят нормально.

Оказалось, я делал вот что: сначала я посылал запрос к устройству, а потом запускал отдельный поток для считывания данных.

Однако поток при старте очищал входной буфер виртуального COM-порта! На всякий случай -- вдруг там мусор какой от предыдущих передач остался?

Если мне везло, то устройство не успевало ответить до очистки буфера, и ошибка не возникала. Поскольку очистка производилась только при старте потока, дальнейшие запросы и ответы воспринимались нормально. Если же мне не везло, то часть ответа (или даже весь ответ) оказывалась стёрта.

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

23:59 

signed char и шестнадцатеричное представление

Допустим, мы хотим увидеть char в шестнадцатеричном виде при помощи printf или аналогичной функции. Это бывает нужно для организации логирования дампов двоичных массивов при приёме/передаче данных, скажем.

Нас ждёт разочарование. char по умолчанию signed. Спецификатор для шестнадцатеричных чисел -- %x. Но он работает с числами размером с int. signed char будет преобразован в unsigned int, и все старшие биты будут забиты единичками из-за дополнительного кода. То есть, вместо 0x2F мы увидим 0xFFFFFF2F (при форматное строке "0x%02x").

Что же делать?
1. Забыть про массивы char. Использовать либо unsigned char, либо новомодный (лет пять-десять как) uint8_t.
2. Использовать двойное преобразование типов. Это лол. Я не ожидал, что это сработает:

printf( "0x%02x ",(unsigned int)(unsigned char)buffer[offset]);

@темы: Программирование

23:59 

Опасность enum

enum -- что может быть лучше?

(для тех кто не в теме)

Чем же это опасно? При любом изменении списка, исключая добавление новых констант в конец, меняется фактическая нумерация! И значение, которое было раньше 2, теперь становится 3. Это совершенно некритично, если доступ к переменным завязан ТОЛЬКО на применении символических значений. Как только происходит выход за пределы данного лягушатника -- начинается беда-печаль. Вот примеры:
1. Бинарное общение с другой программой, у которой список констант немного отличается.
2. Общение самого с собой через файлы. Сохраняем, к примеру, настройки в файл. Обновляем программу, файл считывается, а значение уже не то!
3. Использование символических констант в качестве индексов предопределённых массивов. Например:

enum TypeName {SYMBOLIC_CONSTANT1, SYMBOLIC_CONSTANT2, SYMBOLIC_CONSTANT3};
char SymbolicNames[][80]={"Name1", "Name2", "Name3"};

printf("Name: %s\n",SymbolicNames[SYMBOLIC_CONSTANT2]);

Если модифицировать первый список, по индексам будут выпадать чужие имена.

Что с этим делать:
1. Не модифицировать список в середине и в начале. Никогда. (иногда может показаться, что это можно сделать безболезненно... переупорядочить константы по группам, к примеру)
2. Присваивать принудительные номера. Всегда. (как это делать, см. под катом в начале)
3. Использовать #define .

@темы: Программирование, Говнокод

Untitled

главная