Программистские задачки и хитрости
- MX_Master
- Мастер
- Сообщения: 7476
- Зарегистрирован: 27 июн 2015, 19:45
- Репутация: 3099
- Настоящее имя: Михаил
- Откуда: Алматы
- Контактная информация:
Re: Программистские задачки и хитрости
Кстати, а насколько медленней операции с 64-битными числами по сравнению с 32-битными? На 32-битном процессоре.
- Сергей Саныч
- Мастер
- Сообщения: 9116
- Зарегистрирован: 30 май 2012, 14:20
- Репутация: 2857
- Откуда: Тюмень
- Контактная информация:
Re: Программистские задачки и хитрости
От системы команд зависит. И от реализации этих операций в языках. Конкретно по Cortex'ам не в курсе. Но обычно пересылка-сложение-вычитание раза в 2-3 медленнее. Собственно, проверить нетрудно.MX_Master писал(а):насколько медленней операции с 64-битными числами по сравнению с 32-битными? На 32-битном процессоре.
Чудес не бывает. Бывают фокусы.
- MX_Master
- Мастер
- Сообщения: 7476
- Зарегистрирован: 27 июн 2015, 19:45
- Репутация: 3099
- Настоящее имя: Михаил
- Откуда: Алматы
- Контактная информация:
Re: Программистские задачки и хитрости
Спасибо всем за советы. Оптимизирую прошивку сопроцессора (arisc, Allwinner H3) в ближайшее время.
Есть ещё один теоретический вопрос, на этот раз про аппаратные UART'ы. Насколько я знаю, выходную частоту у этих аппаратных модулей можно довольно гибко менять. Допустим, в микроконтроллере есть ряд аппаратных UART'ов, которые никак не используются. Можно ли (в теории) с помощью аппаратных TX пинов выводить шаги на частотах от 1 до 200000 Гц? - Argon-11
- Мастер
- Сообщения: 2067
- Зарегистрирован: 07 июн 2017, 17:48
- Репутация: 461
- Контактная информация:
Re: Программистские задачки и хитрости
Не знаю, как в H3, но я в одном девайсе на PIC через UART реализовал вывод звука на бипер. Грубо говоря, меандр с произвольной частотой.
Давно программизмом не занимался, не могу сходу въехать - не опасно ли вычитание двух беззнаковых? Особенно после переполнения, когда prevtick > curtick. Какой при этом результат будет?Сергей Саныч писал(а):longtick += curtick - prevtick; // Разность текущего и предыдущего отсчетов прибавим к 64-битному счетчику
-
- Кандидат
- Сообщения: 49
- Зарегистрирован: 27 дек 2017, 10:42
- Репутация: 6
- Настоящее имя: Александр
- Откуда: Брянская область
- Контактная информация:
Re: Программистские задачки и хитрости
да, но:MX_Master писал(а): Можно ли (в теории) с помощью аппаратных TX пинов выводить шаги на частотах от 1 до 200000 Гц?
1. зависит от наличия в МК, в самом аппаратном модуле uart, буфера передатчика. Например, в младший АВР он всего (или целых - как посмотреть) два байта. И тут вам решать: или через прерывания опустошения этого буфера выдавать в него очередные байты (если нужны, конечно), или через цикл гнать нужную последовательность байт
2. Вы выдали 10101010, а uart САМ добавить Стартовый бит в начало, Стоповый - в конец. Если не отключили в настройках, то еще может добавить бит (или 2) четности...
поэтому вы обязаны понимать, что ваша 10101010 превратится в 1101010101 или примерно такое...
- Сергей Саныч
- Мастер
- Сообщения: 9116
- Зарегистрирован: 30 май 2012, 14:20
- Репутация: 2857
- Откуда: Тюмень
- Контактная информация:
Re: Программистские задачки и хитрости
Нормальный результат будет Переполнение счетчика компенсируется переполнением при вычитании. Re: Программистские задачки и хитрости #38Argon-11 писал(а):Давно программизмом не занимался, не могу сходу въехать - не опасно ли вычитание двух беззнаковых? Особенно после переполнения, когда prevtick > curtick. Какой при этом результат будет?
Вот еще пример. Для наглядности переменные возьмем покороче, 4-битные.
Код: Выделить всё
0001 (curtick=1)
-
1110 (prevtick=14)
=
0011 (curtick-prevtick=3)
Чудес не бывает. Бывают фокусы.
- Argon-11
- Мастер
- Сообщения: 2067
- Зарегистрирован: 07 июн 2017, 17:48
- Репутация: 461
- Контактная информация:
Re: Программистские задачки и хитрости
если учесть, что старт-бит обычно это '0', а стоп-бит - '1', то 10101010 превратится в 0101010101, что в общем-то не хужеАлександр Д писал(а): ваша 10101010 превратится в 1101010101 или примерно такое...
Короче, 5-летний перерыв может начисто стереть из памяти правила двоичного вычитанияСергей Саныч писал(а):возьмем покороче, 4-битные.
- MX_Master
- Мастер
- Сообщения: 7476
- Зарегистрирован: 27 июн 2015, 19:45
- Репутация: 3099
- Настоящее имя: Михаил
- Откуда: Алматы
- Контактная информация:
Re: Программистские задачки и хитрости
А ведь множителей частоты может быть и больше. К тому же битрейт тоже настраивается. А для продолжительного постоянного вывода можно прикрутить DMA. Надо попробовать на досуге.Argon-11 писал(а):если учесть, что старт-бит обычно это '0', а стоп-бит - '1', то 10101010 превратится в 0101010101, что в общем-то не хуже
Пример
Код: Выделить всё
0000000001 0000000001 0000000001 0000000001 - 4 шага (х1)
0000100001 0000100001 0000100001 0000100001 - 8 шагов (х2)
0001001001 0001001001 0001001001 0001001001 - 12 шагов (х3)
0101010101 0101010101 0101010101 0101010101 - 16 шагов (х4)
- Serg
- Мастер
- Сообщения: 21923
- Зарегистрирован: 17 апр 2012, 14:58
- Репутация: 5181
- Заслуга: c781c134843e0c1a3de9
- Настоящее имя: Сергей
- Откуда: Москва
- Контактная информация:
Re: Программистские задачки и хитрости
Оглядываясь на стоимость МК считаю это извратом! Хотя если ваше время как программиста ещё дешевле, то почему-бы и нет...MX_Master писал(а):Можно ли (в теории) с помощью аппаратных TX пинов выводить шаги на частотах от 1 до 200000 Гц?
Я не Христос, рыбу не раздаю, но могу научить, как сделать удочку...
- Argon-11
- Мастер
- Сообщения: 2067
- Зарегистрирован: 07 июн 2017, 17:48
- Репутация: 461
- Контактная информация:
Re: Программистские задачки и хитрости
Александр Д не битрейт имел ввиду, а что якобы есть риск получить неровную импульсную последовательность с двумя подряд идущими единицами.MX_Master писал(а):А ведь множителей частоты может быть и больше.
Не пробуй. Делай. Или не делай. Не пробуй. (c) ЙодаMX_Master писал(а):Надо попробовать на досуге.
Скорее всего работать будет, но с нюансами. Например, как на лету менять битрейт (поди еще и без прерываний)? Как задавать кол-во выдаваемых импульсов?
Наверное, все решаемо. Видимо, все параметры должны задаваться в одном сервоцикле и в его пределах быть постоянными?
- Serg
- Мастер
- Сообщения: 21923
- Зарегистрирован: 17 апр 2012, 14:58
- Репутация: 5181
- Заслуга: c781c134843e0c1a3de9
- Настоящее имя: Сергей
- Откуда: Москва
- Контактная информация:
Re: Программистские задачки и хитрости
Перенастройка порта довольно длительная операция во время которой передача невозможна.Argon-11 писал(а):Например, как на лету менять битрейт (поди еще и без прерываний)?
Я не Христос, рыбу не раздаю, но могу научить, как сделать удочку...
- MX_Master
- Мастер
- Сообщения: 7476
- Зарегистрирован: 27 июн 2015, 19:45
- Репутация: 3099
- Настоящее имя: Михаил
- Откуда: Алматы
- Контактная информация:
Re: Программистские задачки и хитрости
Вот о таких нюансах я и спрашивал (: У аппаратного таймера перенастройка проходит быстро. Лично замерял. А про аппаратные UART'ы таких данных не имею.UAVpilot писал(а):Перенастройка порта довольно длительная операция во время которой передача невозможна.
В какой-то степени, дёргать пины в прерываниях на больших частотах довольно странно. Впрочем, как и юзать DMA в роли счётчика шаговUAVpilot писал(а):Оглядываясь на стоимость МК считаю это извратом!
-
- Кандидат
- Сообщения: 49
- Зарегистрирован: 27 дек 2017, 10:42
- Репутация: 6
- Настоящее имя: Александр
- Откуда: Брянская область
- Контактная информация:
Re: Программистские задачки и хитрости
любой опытный и адекватный программер всегда вынужден быть немножечко извращенцем ))) ибо ровно бывает крайне редко! ))
- Сергей Саныч
- Мастер
- Сообщения: 9116
- Зарегистрирован: 30 май 2012, 14:20
- Репутация: 2857
- Откуда: Тюмень
- Контактная информация:
Re: Программистские задачки и хитрости
Насколько помню, PC-шные UART перестраиваются без задержек. А вот у STM только через Disable-Enable.MX_Master писал(а):А про аппаратные UART'ы таких данных не имею.
Но UART плоховато подходит для выдачи произвольной последовательности, именно из-за старт-стопной передачи. В этом плане лучше SPI. Но не менять битрейт на ходу, а транслировать заранее определенную (или вычисленную) последовательность из буфера посредством DMA.
Чудес не бывает. Бывают фокусы.
-
- Мастер
- Сообщения: 362
- Зарегистрирован: 03 сен 2019, 01:31
- Репутация: 37
- Настоящее имя: Александр
- Откуда: Харьков
- Контактная информация:
Re: Программистские задачки и хитрости
USVpilot а jsr -(pc) это не та самая которая потом называлась "чайку попить", название то запомнилось, а вот мануалы рыть..
- Serg
- Мастер
- Сообщения: 21923
- Зарегистрирован: 17 апр 2012, 14:58
- Репутация: 5181
- Заслуга: c781c134843e0c1a3de9
- Настоящее имя: Сергей
- Откуда: Москва
- Контактная информация:
Re: Программистские задачки и хитрости
Вроде не встречал такого названия...
Я не Христос, рыбу не раздаю, но могу научить, как сделать удочку...
-
- Мастер
- Сообщения: 209
- Зарегистрирован: 23 май 2015, 10:47
- Репутация: 49
- Настоящее имя: Юрий
- Откуда: Москва
- Контактная информация:
Re: Программистские задачки и хитрости
Наблюдаемое "верное" поведение является частным случаем классического неопределенного поведения. Любые попытки "доказательства корректности" с помощью компилятора в этом случае не имеют смысла. Компилятор имеет право даже не компилировать этот код.UAVpilot писал(а):Ладно, это было легкогугить.
Вот ещё:
Где ошибка и почему работает?Код: Выделить всё
$ cat t.c #include <stdio.h> void print(int a, int b) { printf("print: a=%d, b=%d\n", a), b; } void main(void) { int x = 5, y = 8; print(x, y); } $ make t cc t.c -o t $ ./t print: a=5, b=8 $
7.21.6.3 The printf function
2) The printf function is equivalent to fprintf with the argument stdout interposed before the arguments to printf
PS: Раз тут был упомянут gcc: при отсутствии флага -ffreestanding возвращаемый тип функции main должен быть 'int'.7.21.6.1 The fprintf function
2) The fprintf function writes output to the stream pointed to bystream, under control of the string pointed to by format that specifies how subsequent arguments are converted for output. If there are insufficient arguments for the format, the behavior is undefined. <---
--
At the nanometer level, the world is made of rubber (с)
At the nanometer level, the world is made of rubber (с)
- Serg
- Мастер
- Сообщения: 21923
- Зарегистрирован: 17 апр 2012, 14:58
- Репутация: 5181
- Заслуга: c781c134843e0c1a3de9
- Настоящее имя: Сергей
- Откуда: Москва
- Контактная информация:
Re: Программистские задачки и хитрости
В данном случае поведение вполне ожидаемое, более того, такой-же набор машинных инструкций сгенерит и сам компилятор из "правильного "текста при некоторых режимах оптимизации.
И с возвращаемым типом всё правильно.
И с возвращаемым типом всё правильно.
Я не Христос, рыбу не раздаю, но могу научить, как сделать удочку...
-
- Мастер
- Сообщения: 209
- Зарегистрирован: 23 май 2015, 10:47
- Репутация: 49
- Настоящее имя: Юрий
- Откуда: Москва
- Контактная информация:
Re: Программистские задачки и хитрости
Конкретное поведение в этом примере находится за рамками языка С. Что именно будет напечатано (и будет ли напечатано вообще) зависит от реализации компилятора, флагов, архитектуры и других факторов.В данном случае поведение вполне ожидаемое
Стандарт языка не дает никаких гарантий при нарушений правил, касающихся неопределенно поведения. Но он гарантирует одинаковое поведение программ, скомпилированных на conforming implementations при отсутствии UB.
Механизм передачи аргументов - он не один, их существует несколько. Что-то применяется чаще всего, что-то реже. Какие-нибудь агрессивные оптимизации могут приводить к передаче аргументов через регистры или как-то иначе. Конкретно в случае с printf у разработчиков компилятора руки немного связаны тем, что они в какой-то степени поддерживают общеиспользуемый ABI. Т.е. ожидать здесь сильно различающегося поведения не приходится. Но вот в случае с user-defined функциями с эллипсисом все не так однозначно. В более сложной программе собственный "аналог" 'printf' может вести себя иначе. Но в любом случае это все спекуляции.
Ключевой момент - "при некоторых режимах оптимизации". А при некоторых - нет. Выкинет 'b', как неиспользуемую с т.з. языка переменную и будет прав, т.к. стандарт дает ему на это право:более того, такой-же набор машинных инструкций сгенерит и сам компилятор из "правильного "текста при некоторых режимах оптимизации
Что собственно и делают большинство компиляторов (в т.ч. и gcc 9.2). Какой либо связи между ‘b’ и тем фактом что ‘printf’ что-то там читает из некоторой области памяти (совершенно неважно откуда именно, хоть стек, хоть регистры), которая содержит значение 'b' с точки зрения стандарта нет. Соответственно ни о каких side эффектах говорить не приходится, хотя вызов ‘printf’ обеспечивает их, но только для явно переданных аргументов.5.1.2.3/4
In the abstract machine, all expressions are evaluated as specified by the semantics. An actual implementation need not evaluate part of an expression if it can deduce that its value is not used and that no needed side effects are produced.
Но вот наблюдаемое поведение при такой оптимизации изменится. И единственный правильный ответ на исходный вопрос - пример содержит неопределенное поведение. О чем явно и говорит стандарт языка.
Еще один, в некоторой степени похожий пример:
Код: Выделить всё
#include <stdio.h>
int main()
{
int* a;
int* b;
int c = (int)(a > b);
printf("%d", c);
}
Но на некоторых процессорах выполнение может привести к падению. Некоторые процессоры могут использовать для хранения адресов специальные адресные регистры и значения этих регистров проверяются при загрузке в них данных (значений указателей). Неинициализированное значение, а точнее содержащее undefined value может представлять из себя trap representaion. Или например(!) процессор может выполнять проверку доступности сегмента в схеме segment-offset.
Стандарт в этом случае просто говорит, что использование таких неинициализированных значений указателей приводит к неопределенному поведению.
Далее: если попытаться написать свой аналог ‘printf’ с эллипсисом, где спецификатору ‘%d’ будет также соответствовать тип ‘int’, то при отсутствии аргумента будет выполнена попытка чтения из области памяти, которая не является областью памяти, где изначально находилось значение типа ‘int’. Это очень сильно похоже на вышеописанный пример со сравнением указателей. Для знаковых скалярных типов тоже могут существовать значения, которые являются trap representation (но для беззнаковых это не так). К сожаленью (или к счастью) такие архитектуры являются очень редкими, поэтому воочию убедиться в этом весьма затруднительно. Но это не отменяет факта их существования.
Стандарт просто определяет перечень легальных способов 'создания' значений некоторого типа (‘int’ в данном случае, или значений указателей). Попытка чтения значений, которые были созданы каким-либо иным способом или интерпретация области памяти как области памяти, которая содержит объект другого типа (здесь есть некоторые нюансы, я пока про ‘int’ и указатели) - приводит к неопределенному поведению. Это как раз и является одним из факторов, который позволяет использовать язык С на разных экзотических архитектурах.
Ну и совсем классический пример, который более пригоден для иллюстрации неопределенного поведения, поскольку "наблюдаемый" результат не совсем очевиден:
Код: Выделить всё
#include <stdio.h>
int main()
{
int i = 5;
i = ++i + ++i;
printf("%d", i);
}
Увы нет. Для того, чтобы сделать такое заключение нужны критерии "правильности".И с возвращаемым типом всё правильно
Вот эта функция "правильная" или нет? (риторический вопрос)
Код: Выделить всё
int plus(int a, int b)
{
return a - b;
}
Код: Выделить всё
int plus(int a, int b)
{
return a + b;
}
Код: Выделить всё
int minus(int a, int b)
{
return a - b;
}
Тоже самое и с ‘main’. Но в этом случае спецификация есть - это стандарт языка. И ни как не компилятор. Более того - стандарт в т.ч. описывает и требования, предъявляемые к компиляторам.
Стандарт определяет две conforming implementation: freestanding и hosted. Первая - это такая 'урезанная' версия для платформ на которой сам компилятор не может быть запущен (в общем случае). Микроконтроллеры, embedded, части ядра и т.п. В частности freestanding реализация не обязана поддерживать все библиотечные функции, которые определены в стандарте.
Вторая - hosted версия, это "полноценная" реализация, которая используется в подавляющем большинстве случаев для прикладных программ. Также в стандарте определены две версии сред выполнения, которые называются аналогично: freestanding и hosted.
Для freestanding в отношении ‘main’ стандарт разрешает все что угодно:
5.1.2.1/1
In a freestanding environment (in which C program execution may take place without any benefit of an operating system), the name and type of the function called at program startup are implementation-defined.
(но с оговоркой что все должно быть задокументировано, см. ниже)5.1.2.1/2
The effect of program termination in a freestanding environment is implementation-defined.
А вот к hosted environment требования совершенно другие:
И существует два лагеря людей, которые по разному понимают этот параграф:5.1.2.2.1/1
The function called at program startup is named main. The implementation declares no prototype for this function. It shall be defined with a return type of 'int' and with no parameters:
int main(void) { /*...*/ }
or with two parameters [...]:
int main(int argc, char *argv[]) { /*...*/ }
or equivalent, or in some other implementation-defined manner.
Первые утверждают что тип возвращаемого значения безоговорочно 'shall be defined with a return type of 'int' (причем значение слова 'shall' в стандарте оговаривается отдельно, как самое сильное требование, нарушение которого приводит к UB(параграфы 4/1, 4/2). И при этом последняя часть - 'or in some other implementation-defined manner' относится к дополнительным параметрам ‘main’, вроде такого:
Код: Выделить всё
int main (int argc, char *argv[], char *env[])
Если с первым вариантом все понятно - нельзя и все, то второй переносит ответственность на реализацию.
Что из этого следует: во первых - любое использование implementation-defined аспектов компилятора выводит программу из категории 'strictly conforming program' (параграф 5/4) и она сразу становится зависимой от конкретного компилятора и опционально от флагов компиляции.
'strictly conforming program' - это весьма жесткое ограничение, но следствием является тот факт, что такие программы являются наиболее переносимыми между компиляторами, т.к. не зависят от implementation-defined. Поэтому любой пример кода, в котором явно дополнительно не указан компилятор и ключи компиляции (именно таким был исходный пример), я рассматриваю как strictly conforming program.
Во вторых - нужно принимать во внимание еще один пункт стандарта - 5/8:
И у GCC такой документ есть. https://gcc.gnu.org/onlinedocs/gcc-9.2.0/gcc.pdfAn implementation shall be accompanied by a document that defines all implementation-defined and locale-specific characteristics
Там в частности описан поддерживаемый список языков и версий стандартов.
Нас интересует ISO/IEC 9899:2018 поскольку именно он является текущим стандартом языка С (а стандарт используемый в примере не был указан). Для текущего стандарта нужен ключ '-std=iso9899:2017'
Далее:
Ключ ‘-std’ отключает использование extensions. На упомянутой странице 451 в Chapter 6 имеется следующее:By default, GCC provides some extensions to the C language that, on rare occasions conflict with the C standard. See Chapter 6 [Extensions to the C Language Family], page 451. Some features that are part of the C99 standard are accepted as extensions in C90 mode, and some features that are part of the C11 standard are accepted as extensions in C90 and C99 modes. Use of the ‘-std’ options listed above disables these extensions where they conflict with the C standard version selected.
Если попытаться откомпилировать ‘void main(){}’ с ключем '-pedantic', то возникает следующее предупреждение:GNU C provides several language features not found in ISO standard C. (The ‘-pedantic’ option directs GCC to print a warning message if any of these features is used.)
Что это значит? А это значит, что такая language feature not found in ISO standard C.<source>:3:6: warning: return type of 'main' is not 'int' [-Wmain]
Таким образом GCC не только не разрешает явное использование ‘void main()’, но и явно указывает на то, что такая возможность не совместима со стандартом языка.
Более того, в этом же документе можно найти и другие подтверждения этому:
Плюс здесь указан тот факт, что по умолчанию используется hosted implementation, а не freestanding (что абсолютно логично).The standard also defines two environments for programs, a freestanding environment, required of all implementations and which may not have library facilities beyond those required of freestanding implementations, where the handling of program startup and termination are implementation-defined; and a hosted environment, which is not required, in which all the library facilities are provided and startup is through a function
int main(void)
or
int main (int, char *[]).
An OS kernel is an example of a program runningin a freestanding environment; a program using the facilities of an operating system is an example of a program running in a hosted environment. GCC aims towards being usable as a conforming freestanding implementation, or as the compiler for a conforming hosted implementation. By default, it acts as the compiler for a hosted implementation.
Помимо этого в описании опции '-Wmain' указано следующее:
Т.е. разработчики GCC явно принадлежат к той категории, которая трактуют несколько неоднозначную фразу из параграфа стандарта 5.1.2.2.1/1 как явный запрет 'void' в качестве возвращаемого типа.Warn if the type of main is suspicious. main should be a function with external linkage, returning int, taking either zero arguments, two, or three arguments of appropriate types. This warning is enabled by default in C++ and is enabled by either ‘-Wall’ or ‘-Wpedantic’.
Соглашаться в контексте всего этого с фразой "с возвращаемым типом всё правильно" я определенно не могу. "Правильно" может быть в соответствии с чем-то. 'void main' в случае с gcc - это "не правильно" в соответствии со стандартом языка (и, косвенно, в соответствии с документом от GCC, который требуется, опять же - по стандарту языка). Совершенно не имеет значения тот факт, что при некоторых флагах компилятора это "работает", компиляторы вынуждены много чего поддерживать для legacy кода.
--
At the nanometer level, the world is made of rubber (с)
At the nanometer level, the world is made of rubber (с)
- Serg
- Мастер
- Сообщения: 21923
- Зарегистрирован: 17 апр 2012, 14:58
- Репутация: 5181
- Заслуга: c781c134843e0c1a3de9
- Настоящее имя: Сергей
- Откуда: Москва
- Контактная информация:
Re: Программистские задачки и хитрости
Много умных слов - это круто, но к данному примеру никакого отношения не имеет, ибо в нём ни о каких опциях gcc не говорится, т.е. используем gcc "по дефолту". Иначе вообще ни очём говорить не стоит, ибо этими опциями можно добиваться совершенно противоположных результатов - таков gcc.
Касательно примера повторю: не смотря на якобы синтаксическую ошибку gcc и множество других компиляторов С сгенерят совершенно законный и рабочий код.
Некоторые режимы оптимизации - это например оптимизация по скорости, по размеру кода и т.п. И большинство их сгенерят аналогичный код.
Касательно примера повторю: не смотря на якобы синтаксическую ошибку gcc и множество других компиляторов С сгенерят совершенно законный и рабочий код.
Некоторые режимы оптимизации - это например оптимизация по скорости, по размеру кода и т.п. И большинство их сгенерят аналогичный код.
Я не Христос, рыбу не раздаю, но могу научить, как сделать удочку...