Как стать автором
Обновить
45
0.7
Иван Савватеев @SIISII

Микроконтроллеры, цифровая электроника, ОС…

Отправить сообщение

Чем меньше возможностей для случайных ошибок, тем лучше. Тем более, что путаница между 0 и o, c и с, l и 1 нередко приводит-таки к возникновению ошибок при компиляции (скажем, неизвестный идентификатор). Паскаль препятствует возникновению подобных ошибок, C допускает массу случаев, где они возможны.

Ну, я сравниваю, главным образом, чистый C и чистый Паскаль, причём Паскаль появился на пару лет раньше -- но не имеет основной массы проблем, которые имеются в C. Изрядная часть кривизны С++ -- это следствие попыток сохранить совместимость с чистым С вместо того, чтобы выкинуть её полностью и делать нормальный язык с читабельным синтаксисом и строгой типизацией.

Я в последнем абзаце указал, что сейчас почти все проблемы решаются включением всех предупреждений и превращением их в ошибки -- но (1) эта возможность появилась не так уж давно (напомню, чистый С -- самое начало 1970-х, С++ -- 1980-е) и (2) это, по сути, костыль, смягчающий кривизну языка. Собственно, мой пост как раз про то, что С/С++ -- изначально кривые, откуда и возникает множество ошибок на пустом месте.

Проблемы есть и в C/C++ -- эта сладкая парочка просто провоцирует возникновение ошибок на пустом месте.

Например, изначально, ещё в чистом C, регулярно возникали большие проблемы из-за отсутствия логического типа. Такие ошибки, как & вместо && или наоборот, в Паскале невозможны в принципе. (Замечу попутно, что введение bool в C++ ничем реально не помогло, поскольку типизация как была слабой, так и осталась -- сплошные автоматические преобразования без всякого реального контроля).

Ещё одна проблема -- присваивание рассматривается как операция, а не оператор, что приводит к if (a = b) вместо if (a == b).

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

А ещё приснопоминаемый break, который нужно явным образом указывать в case...

А ещё до самого 2011-го года не было стандартизации размеров целочисленных типов. Правда, в Паскале этого тоже нет -- но язык создавался как учебный. Вот в Аде есть, правда, оформлено по-другому.

Плюс, запутанный и неудобочитаемый синтаксис. Увидишь * и не можешь сразу, без анализа, понять, что это -- умножение или разыменование указателя? Или int *A[10] -- это массив указателей или указатель на массив? Понятно, что постоянное использование языка смягчает эти проблемы, но читабельность в любом случае хуже паскалеподобных языков, которые куда однозначнее, а заодно не допускают слишком уж извращённых синтаксических конструкций.

Да, сейчас большая часть потенциальных проблем в C/C++ более-менее решается включением всех предупреждений и превращением их в ошибки, но когда-то такой возможности не было; кроме того, подобные костыли нужны для обхода/смягчения изначальной кривизны самого языка. Так что реальная проблема изначально кроется всё ж и в языке тоже, а не только в его "гибкости" -- ту же самую гибкость и функциональность, что имеет C, имеет и классический Паскаль (и точно так же имеет проблемы с утечками памяти или использованием её после освобождения -- поскольку вот эту проблему без ущерба гибкости, функциональности и производительности в полном объёме решить как раз невозможно).

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

Можно писать безопасный код на чём угодно, и небезопасный тоже можно на чём угодно написать. Вопрос лишь в том, насколько просто или сложно это сделать на том или ином языке.

В некоторых случаях не нужна, да. Скажем, массив (в частности, вектор) может хранить, например, указатели на какие-то структуры, которые формирует один поток, а обрабатывает другой. Для выборки и изменения отдельного элемента массива, т.е. одного указателя, достаточно атомарной операции сравнения с обменом или чего-то подобного без какой-либо явной межпоточной синхронизации. Но, понятно, такой номер проходит, если обработка поэлементная, не требующая блокировки всего вектора.

Ну, полноценных шахмат на МК-61 не было и быть не могло. Кажется, сделали эндшпиль, когда на доске по паре-тройке фигур, но за давностью лет не помню. А вообще, шахматные программы достаточно давно появились, см, например, https://ru.wikipedia.org/wiki/Каисса_(программа)

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

Ну а с ОС и кэшами и другие проблемы могут быть -- зависит от особенностей работы ОС (в мире не только Винда с Линухом существуют) и архитектуры процессора.

Вот в таких задачах ручное программирование на ассемблере (ну или использование специфичных для конкретного компилятора и архитектуры расширений языка, сводящихся к ассемблеру) и даёт иногда колоссальный прирост производительности. Конечно, ценой непереносимости кода, но всегда ли она нужна, не говоря о том, что ничто не мешает предлагать как переносимую реализацию, так и специфичные для конкретных платформ?

Вообще-то, UE5 -- прямое продолжение UE4, и 95% кода у них идентичны. Номер версии UE меняют больше из маркетинговых, а не технических соображений, и между UE 4.1 и 4.26 разницы куда больше, чем между 4.26 и 5.0.

Ну и русский язык стоит подтянуть. "Не желе" -- это в каком смысле, "не желе, а холодец"?

Драйвер -- это, вообще говоря, либо простая железяка, которая "качает" другую железяку (скажем, драйвер двигателя), либо программа, реализующая логику управления чем-либо. Здесь, думается, корректней было бы говорить о контроллере семисегментника и т.д., а не о драйвере. Но это так, придирки :)

Судя по приведённым картинкам, речь про какой-то ATSAMx7? Если так, то использовать SDRAM, по сути, невозможно: контроллер столь забагован, что его официально признали непригодным:

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

Вы отрицаете, что старший бит целого числа, представленного в дополнительном коде, является битом знака? Вы отрицаете, что нулевое значение знакового бита соответствует знаку "плюс", а единичное -- знаку "минус"? Вы отрицаете, что число, имеющее значение 0, имеет знаковый бит, равный нулю, что означает знак "плюс"? Наконец, Вы отрицаете, что нуль как вещественное число имеет знак? И как последнее согласуется с Вашим утверждением, что "Ноль он и в Африке ноль", когда это прямо противоречит математике?

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

А, например, в PDP-11 и ARM этот бит называется N (понятное дело, от negative), а для обозначения условия перехода используется сочетание PL -- от plus, естественно:

Как видите, в этом отрывке (из описания архитектуры ARMv7-M) специально подчёркивается, что "плюс" -- это положительное число или нуль, а не только "настоящее" положительное число.

В общем, компьютерный мир намного шире, чем Ваше (и моё, да) представление о нём, и абстрактно-математическим концепциям он иногда не соответствует.

В MIPS, кажется, тоже. А в IBM System/360 система переходов другая, поэтому там целочисленного знакового нуля, с точки зрения программиста, тоже нет: там ещё не четыре традиционных флажка, а двухбитовый код условия, правила установки которого зависят от типа выполняющейся команды. Поэтому тамошняя система команд имеет отдельные команды сложения-вычитания для знаковых чисел и для беззнаковых: сам результат, понятно дело, идентичен, отличается лишь установка кода условия. Но появились эти машинки ещё в середине 1960-х, у основной массы более современных -- те самые четыре флажка, что оказалось наиболее удобным для программиста.

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

Вообще, в большинстве архитектур так, включая IA-32 (x86) и ARM. Ну а вещественный нуль вообще везде знак имеет, похоже.

И, кстати, если уж касаться "усложнения разработки процессоров", то неплохо было бы спуститься на уровень схемотехники и показать, чем так хорош дополнительный код (а он действительно хорош, иначе б его не использовали). Правда, сразу возникает вопрос: а почему в вещественных числах используют прямой код (и имеют положительный и отрицательный нули)? В общем, улучшать есть куда.

Потому что с результатом операции (в том числе сравнения или проверки значения) связаны, как правило, четыре пары атрибутов: нуль/не нуль, плюс/минус, перенос/нет переноса, переполнение/нет переполнения. Скажем, если сравниваются два одинаковых числа, будут получены признаки "нуль" (он же равно: сравнение выполняется путём вычитания, только результат собственно вычитания теряется, остаются лишь признаки результата) и "плюс" (поскольку старший бит результата равен нулю). Если сразу после сравнения выполнить команду "переход, если результат положительный", переход произойдёт -- т.е. равенство (нулевой результат вычитания) будет воспринято как положительное число. Это хорошо видно, если писать на ассемблере (см., например, регистр флагов в архитектуре IA-32, она же x86, или регистр состояния процессора в архитектуре ARM).

Информация

В рейтинге
1 666-й
Откуда
Солнечногорск, Москва и Московская обл., Россия
Дата рождения
Зарегистрирован
Активность

Специализация

Embedded Software Engineer
Lead