company_banner

И всё же C — низкоуровневый язык


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


    Критика C — классический для нашей индустрии эпистолярный жанр. Она звучит то громче, то тише, но в последнее время буквально оглушает. Пример — перевод статьи Дэвида Чизнэлла «C — не низкоуровневый язык», опубликованный в нашем блоге некоторое время назад. Про C можно говорить разное, в дизайне языка действительно много неприятных ошибок, но отказывать C в «низкоуровневости» — это уже слишком!


    Чтобы не терпеть такую несправедливость, я собрался с духом и постарался определиться с тем, что есть язык программирования низкого уровня и чего хотят от него практики, после чего перебрал аргументы критиков C. Так получилась эта статья.


    Содержание



    Аргументы критиков C


    Вот некоторые аргументы критиков С, перечисленные в том числе и в статье Дэвида Чизнэлла:


    1. Абстрактная машина языка C слишком похожа на устаревшую архитектуру PDP-11, которая давно уже не соответствует устройству популярных современных процессоров.
    2. Несоответствие абстрактной машины C устройству реальных машин усложняет разработку оптимизирующих компиляторов языка.
    3. Неполнота и сложность стандарта языка ведут к разночтениям в реализациях стандарта.
    4. Доминирование C-подобных языков не позволяет исследовать альтернативные архитектуры процессоров.

    Давайте для начала определимся с требованиями к низкоуровневому языку, после чего вернёмся к приведённым аргументам.


    Язык программирования низкого уровня


    Общепризнанного определения языка низкого уровня не существует. Но перед обсуждением спорных моментов желательно иметь хоть какие-то исходные требования к предмету спора.


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


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


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


    Поиск баланса между этими двумя требованиями — выделение общих для платформ аспектов и доступ к по возможности большему числу деталей — фундаментальная причина сложности разработки языка низкого уровня.


    Заметьте, что для такого языка не так важны высокоуровневые абстракции, — для него важнее служить контрактом между платформой, компилятором и разработчиком. А если есть контракт, то возникает необходимость в независимом от конкретной реализации стандарте языке.


    Наше первое требование — общие для целевых платформ черты — выражается в абстрактной машине языка, поэтому с неё мы и начнём обсуждение C.


    Дело не только в PDP-11


    Платформа, на которой появился язык C, — PDP-11. В её основе лежит традиционная архитектура фон Неймана, в которой программы выполняются последовательно центральным процессором, а память представляет собой плоскую ленту, где хранятся и данные, и сами программы. Такая архитектура легко реализуется в железе, и со временем все компьютеры общего назначения стали использовать именно её.


    Современные усовершенствования архитектуры фон Неймана направлены на устранение её главного узкого места — задержек при обмене данными между процессором и памятью (англ. von Neuman bottleneck). Разница в производительности памяти и центрального процессора привела к появлению кеширующих подсистем процессоров (одноуровневых и позже — многоуровневых).


    Но даже кешей в наши дни уже недостаточно. Современные процессоры стали суперскалярными (англ. superscalar). Задержки при получении инструкциями данных из памяти частично компенсируются внеочередным выполнением (англ. instruction-level parallelism) инструкций вкупе с предсказателем ветвлений (англ. branch predictor).


    Последовательная абстрактная машина C (и многих других языков) имитирует работу не столько конкретно PDP-11, сколько любых компьютеров, устроенных по принципу архитектуры фон Неймана. К нему относятся архитектуры, построенные вокруг процессоров с единственным ядром: настольные и серверные x86, мобильные ARM, сходящие со сцены Sun/Oracle SPARC и IBM POWER.


    Со временем в один процессор стали интегрировать несколько вычислительных ядер, в результате чего появилась необходимость поддерживать когерентность кешей каждого ядра и потребовались протоколы межъядерного взаимодействия. Архитектура фон Неймана, таким образом, была масштабирована на несколько ядер.


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


    Таким образом, утверждение о том, что абстрактная машина C давно не соответствует устройству современных процессоров, касается не столько конкретного языка, сколько компьютеров, использующих архитектуру фон Неймана, в том числе и в параллельном исполнении.


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


    Стандартизированное и переносимое воплощение архитектуры фон Неймана — абстрактная машина C — удобно реализуется на всех основных платформах и поэтому пользуется своей популярностью как портативного ассемблера вполне заслуженно.


    Оптимизирующие компиляторы и язык низкого уровня


    Наше второе требование к языку низкого уровня — доступ к низкоуровневым деталям реализации каждой из популярной платформ. В случае C это непосредственная работа с памятью и объектами в ней как с массивом байтов, возможность напрямую работать с адресами байтов и развитая арифметика указателей.


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


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


    Позиция разработчиков C в данном случае такова: низкоуровневый язык должен позволять работать на уровне, достаточно низком для самостоятельного решения программистом задач оптимизации. В рамках C возможно поработать компилятором, выбрав, к примеру, инструкции SIMD и правильно разместив данные в памяти.


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


    Интересно, что Чизнэлл в статье под названием «C — не низкоуровневый язык» парадоксально утверждает, что C — слишком низкоуровневый, указывая на отсутствие в нём высокоуровневых инструментов. Но практикам бывают нужны именно низкоуровневые инструменты, иначе язык не получится использовать для разработки операционных систем и других низкоуровневых программ, то есть он не будет удовлетворять второму из наших требований.


    Отвлекаясь от описания проблем оптимизации именно C, хочу заметить, что в настоящий момент в оптимизирующие компиляторы высокоуровневых языков (тех же C# и Java) вложено не меньше усилий, чем в GCC или Clang. У функциональных языков тоже хватает эффективных компиляторов: MLTon, OCaml и другие. Но разработчики того же OCaml пока могут похвастаться производительностью в лучшем случае в половину скорости кода на C


    Стандарт как безусловное благо


    Чизнэлл приводит в своей статье ссылку на результаты опроса, проведённого в 2015 году: многие программисты допускали ошибки в решении задач на понимание стандартов C.


    Полагаю, что кто-то из читателей имел дело со стандартом C. У меня версия C99 есть в бумажном виде, страниц эдак на 900. Это не лаконичная спецификация Scheme объёмом меньше 100 страниц и не вылизанный Standard ML, состоящий из 300. Удовольствие от работы со стандартом C не получает никто: ни разработчики компиляторов, ни разработчики документа, ни программисты.


    Но надо понимать, что стандарт C разрабатывался постфактум, уже после появления множества «почти-еле-только местами» совместимых диалектов. Авторы ANSI C проделали огромную работу, обобщив существующие реализации и прикрыв бесчисленными «костылями» неортогональности в дизайне языка.


    Может показаться странным, что такой документ вообще кто-то взялся реализовывать. Но C был реализован множеством компиляторов. Я не буду пересказывать чужие байки о зоопарке мира UNIX конца 80-х, тем более что сам в то время считал не слишком уверенно и только до пяти. Но, очевидно, стандарт был всем в индустрии действительно нужен.


    Прекрасно то, что он есть и реализован по меньшей мере тремя крупными компиляторами и множеством компиляторов поменьше, в совокупности поддерживающими сотни платформ. Ни один из языков — конкурентов C, претендующих на корону короля языков низкого уровня, не может похвастаться таким многообразием и универсальностью.


    На самом деле современный стандарт C не так уж и плох. Более-менее опытный программист способен разработать неоптимизирующий компилятор C в разумные сроки, что подтверждается существованием множества полулюбительских реализаций (тех же TCC, LCC и 8cc).


    Наличие общепринятого стандарта означает, что C удовлетворяет последнему из наших требований к языку низкого уровня: этот язык строится от спецификации, а не конкретной реализации.


    Альтернативные архитектуры — удел специальных вычислений


    Но Чизнэлл приводит ещё один аргумент, возвращаясь к устройству современных процессоров общего назначения, реализующих варианты архитектуры фон Неймана. Он утверждает, что имеет смысл изменить принципы работы центрального процессора. Повторюсь, что эта критика касается не конкретно C, а самой базовой модели императивного программирования.


    Действительно, существует множество альтернатив традиционному подходу с последовательным исполнением программ: модели SIMD в стиле GPU, модели в стиле абстрактной машины Erlang и другие. Но каждый из этих подходов имеет ограниченную применимость при использовании в центральном процессоре.


    GPU, например, замечательно перемножают матрицы в играх и машинном обучении, но их сложно использовать для трассировки лучей. Другими словами, эта модель подходит для специализированных ускорителей, но не работает для процессоров общего назначения.


    Erlang прекрасно работает в кластере, но эффективную quick sort или быструю хеш-таблицу на нём сделать трудно. Модель независимых акторов лучше использовать на более высоком уровне, в большом кластере, где каждый узел — всё та же высокопроизводительная машина с традиционным процессором.


    Между тем, современные x86-совместимые процессоры давно уже включают в себя наборы векторных инструкций, схожие с GPU по назначению и принципам работы, но сохраняющие общую схему процессора в стиле фон Неймана в целом. Не сомневаюсь, что любые достаточно общие подходы к вычислениям будут включены в популярные процессоры.


    Есть такое авторитетное мнение: будущее за специализированными программируемыми ускорителями. Под такие неординарные железки действительно имеет смысл разрабатывать языки с особой семантикой. Но компьютер общего назначения был и остаётся похожим на ту самую PDP-11, для которой так хорошо подходят C-подобные императивные языки.


    С будет жить


    В статье Чизнэлла есть фундаментальное противоречие. Он пишет, что для обеспечения скорости программ на C процессоры имитируют абстрактную машину C (и давно забытую PDP-11), после чего указывает на ограниченность такой машины. Но я не понимаю, почему это означает, что «C — не низкоуровневый язык».


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


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


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


    Так или иначе, нам по-прежнему нужен язык низкого уровня, причём построенный именно для популярных фоннеймановских компьютеров. И пускай C устарел, но, видимо, любому его преемнику всё равно придётся отталкиваться от тех же самых принципов.

    Badoo
    381,26
    Big Dating
    Поделиться публикацией

    Похожие публикации

    Комментарии 185

      +11

      С таки язык высокого уровня, поскольку он создает слой абстракции над железом, позволяя вместо инструкций конкретного процессора и его регистров использовать более общий набор команд и более приближенную к человечесаому форму представления программы.


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

        +9

        "высокий" или "низкий" — понятия относительные. Относительно большей части прикладных (и системных ) приложений C очень даже низкий: низкоуровневые интерфейсы ОС определены в терминах С, сама ОС, как правило, написана на С и так далее.


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


        А оттуда уже и до микрокода рукой подать…

          +8
          Поскольку С как таковой не может гарантировать ни время исполнения транслированных инструкция, ни их расположение в памяти (без ассемблерных вставок) то языком низкого уровня он однозначно не является.
            0

            Это вопрос "достаточной низкоуровневости".


            Для ОС в основном, как мы на практике знаем, вполне достаточно тех средств, что предоставляет C (с некоторыми общепринятыми нестандартными расширениями).


            Но может не хватить тех средств, что предоставляет, скажем, Java. Хотя использовать высокоуровневые языки в этой роли, конечно, были :-) Мы все знаем, где они, эти попытки, оказались.

              –7
              Уже само наличие возможности делать ассемблерные вставки, даже с сахаром, говорит о том, что С — низкоуровневый.
              А машинные коды это тоже абстракция над железом, так что либо мы отказываем в существовании низкоуровневых языков вообще, либо убираем этот критерий.
                +4

                Turbo Pascal получается тоже низкоуровневый?

                  +3
                  Да, в версиях ниже 5.5 TP находится на том же уровне, что и С.
                    +1
                    В этом вашем Си, в отличие от Паскаля, нет самостоятельных перечислимых типов, диапазонов, множеств, вложенных функций, вдобавок паскалевые массивы и строки знают о своей длине.
                    0

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

                      0
                      Так можно сказать, что любой язык компилирующийся в нативный код: суть макроассемблер на стероидах.
                        0
                        И где же они там спрятаны? В ТП были вполне полноценные указатели, появилась арифметика указателей, были массивы для доступа в произвольное место памяти (Mem, MemW и MemL, синтаксис: Mem[segment:offset]), массивы для доступа к портам (Port, PortW) и даже директива inline для вставок машинного кода (не ассемблера, а именно машинного, в виде чисел).

                          0

                          да, все есть, я не спорю. (Турбо) Паскаль нисколько не уступал C.


                          Но язык рассказывается явно не этот этих возможностей. В Паскале традиционно упор делался на статическую аллокацию ресурсов, указатели можно вообще не использовать. Что, быть может, даже хорошо.


                          В отличие от Си, где без указателей даже шевельнуться нельзя.

                            0
                            Ну, не знаю. Турбо Паскаль был моим вторым языком (первым был ассемблер). Разок попробовал C и желания вернуться на ТП больше не возникло. Впрочем, мне вообще не нравился Филип Кан… )))
                              +1
                              Мне в сях часто не хватает множеств, нормальных строк, нормального switch/case, вложенных функций и иногда возможности установить любой диапазон индексов у массива.

                              Красиво же:
                              const
                                map: array ['A'..'F'] of byte = (10, 11, 12, 13, 14, 15);

                              или
                              if s[i] in ['a'..'f', 'A'..'F'] then ...
                            0
                            Статических массивов было достаточно разве что школьникам, когда в подпрограмму надо было передать массив заранее неизвестной длины передавали указатель, пока не появилось динамических массивов.
                              0

                              Удивительно, но статических массивов очень долго хватало даже программистам на Фортране… :-)


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

                                0
                                Есть все, что нужно для работы с указателями: взятие адреса, разыменование, арифметика. Причем тут «скрытые приведения типов»?
                                  0

                                  Да не переживайте вы так, я с глубоким уважением отношусь к Паскалю. :-)


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

                      +6

                      Не составит большого труда добиться ассемблерных вставок на хаскеле (inline-c, а дальше все понятно). Следует ли из этого, что хаскель низкоуровневый?

                        0

                        Про хаскель сложный вопрос, я склоняюсь считать его применимым для системного программирования, хотя и с рядом серьёзных ограничений.
                        А кто низкоуровневый?
                        Вон в js/java/php нельзя вставить ассемблер, они точно нет.

                          +1
                          Вон в js/java/php нельзя вставить ассемблер, они точно нет.

                          В Java можно, вероятно в php и js на node.js тоже есть способы вставить нативный код. Практически любой язык (кроме браузерных) позволяет вставки C кода и вызовы С библиотек, а от него через него и асемблера.
                            +1
                            Мне кажется низкоуровневый язык или нет определить проще простого. Если можно на нем написать bootloader — низкоуровневый, если нет — то нет.
                              +1
                              на C++ можно написать bootloader
                                +1
                                Если можно на нем написать bootloader — низкоуровневый, если нет — то нет.

                                На Java можно написать bootloader, более того они уже написаны. В реальности, практически на любом языке можно написать bootloader, для языков, требующих виртуальной машины, придется повозится с компилятором, но все равно — можно.
                                0

                                Насчёт джавы вы неправы. По ссылке по сути способы с помощью программы на Java сгенерировать ассемблерный код, а потом результат слинковать динамически с текущим рантаймом. Если у языка имеется интероп а нативным кодом, то такую конструкцию построить всегда можно, но это не является ассемблерный вставкой само по себе. Да и интероп у джавы довольно поганенький.

                            0
                            тут недавно статья была, т.е. шарп низкоуровневый?
                              0
                              И BASIC низкоуровневый, у него вообще можно вставки шестнадцатеричного кода делать)))
                              +1

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


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

                                +4
                                Строго говоря, написание программы на ассемблере или напрямую в машинных кодах тоже не может гарантировать время исполнения инструкций на современных х86. Поскольку есть такая штука, как Intel ME, имеющий доступ ко всей памяти и имеющий возможность залезть туда в любой момент. А поскольку шина памяти общая, то время доступа Intel ME отнимет кусочек от работы основного, написанного на ассемблере, блока. Причем произвольный доступ в SDRAM — это далеко не один такт. Далее, вследствие этого доступа, пойдут изменения в таблицах LRU для кэша и т.п.
                                Скорее всего, соотношение между временем работы Intel ME и основных ядер — один к миллиону. Но в любом случае, точно гарантировать ничего нельзя, даже на bare metal, без операционки.
                              +3

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

                                +2

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


                                Во-первых, тогда вообще нет смысла говорить "язык низкого уровня", их как бы тогда нет.


                                Во-вторых, с тех пор случились 80-ые, за которые разработка системных приложений и ОС перешла с ассемблера на портативный код на С, и последний стали рассматривать как основной низкоуровневый язык.

                                  +1
                                  Во-первых, тогда вообще нет смысла говорить "язык низкого уровня", их как бы тогда нет.

                                  Как нет? А язык ассемблера ____ (подставить нужное)?

                                    +1

                                    В ваше определение подходят только языки ассемблера? Ну, может быть.


                                    Но, согласитесь, никто уже сто лет как не пишет на ассемблере, только в крайне узких областях. И как-то ранжировать языки все же придется по степени близости к железу, нет? Условно, тех, на которых имеет смысл драйвера и ОС писать, и тех, на которых пишут веб-сервисы?

                                      +1

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

                                        0
                                        Я только вчера писал на ассемблере)
                                        в esp32 есть сопроцессор и для него только ассемблер
                                        docs.espressif.com/projects/esp-idf/en/latest/api-guides/ulp.html
                                          0

                                          Да, побаловаться с маленькими бывает очень весело :-) И в мире микроконтроллеров до сих пор частенько пишут на языках ассемблера, спорить не буду. "Частенько" в смысле "чаще, чем в среднем по палате".


                                          Но все же очень много С и еще, говорят, Форт местами. И именно некоторые их низкоуровневые аспекты позволяют это делать.

                                            0
                                            а вообще многие в микроконтроллерах уже на C++ пишут. ну в таких где cortex уже какой нибудь. Даже micropython встречается
                                              0

                                              Это да, мир микроконтроллеров очень многооборазен.

                                                0
                                                Современные микроконтроллеры мощнее первых пентиумов, четкой границы нет, как это понималось в 90е годы, например, когда Microchip/Atmel были преимущественно, с 64 байтами оперативки.
                                                  0

                                                  Так точно, я про это и говорил. Я бы не взялся какие-то конкретные параметры называть, т.к. устройств — огромное количество, с самыми разными интерфейсами и возможностями.

                                                0

                                                Не обязательно кортекс. Спокойно писал под attiny на плюсах в 2008-м.

                                            0
                                            Узкие это не связанные с обычным ПО на ПК? Ибо у меня добрая половина знакомых что пишут под микроконтроллеры используют асм.
                                              0

                                              Да, частенько приходится и на асмах, насколько мне известно. Но еще чаще встречаются диалекты Си, нет?

                                              0
                                              В ваше определение подходят только языки ассемблера?

                                              На данный момент — да, если забыть про машинные коды.
                                              Но с развитием железа все может поменяться.


                                              И как-то ранжировать языки все же придется по степени близости к железу, нет?

                                              Ранжировать языки надо, но это не повод перекраивать сложившуюся терминологию.
                                              Что мешает классифицировать ЯВУ по близости к железу без именования какого-либо из них низкоуровневым?
                                              Можно назвать С, например, языком системного программирования.

                                                0

                                                Признаться, в контексте статьи это не важно :-) Системный так системный.


                                                Вообще, Чизнэлл не об этом писал, а я, в свою очередь, полемизировал именно с ним. Думаю, он мог бы вполне осмысленно назвать статью иначе: "Архитектура фон Неймана и следующая из нее модель программирования устарели". Но это не так сильно звучит, как "популярный в определенных областях язык — плохой"

                                          +6
                                          Тогда и ассемблер — не язык низкого уровня. Страничная адресация, risc-ядро, переименовывание регистров на лету и уже не знаешь, ни где лежит, ни что исполняется, ни сколько времени команде(ам) нужно.
                                            –1

                                            Это если говорить о современном x86 ассемблере и наследниках. Есть архитектуры где что написано — то и исполняется.

                                              0

                                              Внутренняя кухня микропроцессора — это, конечно, интересно, но для программиста это вполне конкретный набор регистров и инструкций. И в ассемблере никаких абстракций, выходящих за рамки этих инструкций.
                                              А страничная адресация — вполне себе аппаратная штука, управляемая командами процессора и регистрами. Всегда есть код, из которого при желании можно понять, что и где выполпяется (в BIOS или ОС).

                                                0

                                                Почему же нет, есть макросы. Тот же masm позволяет навертеть "сахара" поверх голых инструкций.

                                                  0

                                                  Тогда уж можно вспомнить и турбоассемблер с некоторой толикой поддержки ООП.
                                                  Но тут все сводится к упаковке некоторого текста в макроопределения, чтобы не вводить текст повторно, некоторым расширениям к именованию областей кода и данных, а также управлению компиляцией, позволяющему по условию исключать или добавлять ассемблерный код в итоговый компилируемый в машинные инструкции текст.
                                                  Непосредственно в языке дополнительных возможностей, выходящих за мнемокоды операций процессора не было.

                                                    0
                                                    Да, но отсутствие высокоуровневых конструкций — достаточное условие, чтобы признать язык низкоуровневым. Но не необходимое.
                                                    0

                                                    Или вот еще есть такая игрушка: https://en.wikipedia.org/wiki/High_Level_Assembly

                                                +1
                                                единственный низкоуровенвый язык — машинные коды
                                                А оттуда уже и до микрокода рукой подать…

                                                Особенно на x86, где машинный код тоже высокоуровневый, так как на уровне проца это просто API для копилятора...

                                                +1
                                                C имеет самый низкий уровень из тех языков высокого уровня, что используются сейчас, но существуют и более низкоуровневые языки высокого уровня — например C--.
                                                  +2

                                                  Классификации языков на низко- и высокоуровневые уже полвека, и эта классификация попросту изжила себя. Какой смысл в этом классификации, если практически все современные языки по этой классификации высокоуровневые?


                                                  Более правильно говорить о возможностях. В том же C вполне себе имееются низкоуровневые возможности: преобразование указателя к числу, возможность использования интринсиков для работы с определёнными регистрами и т.д.


                                                  Я бы классифицировал языки по возможностям:


                                                  1. Работа с регистрами процессора, с железом.
                                                  2. Минимиальные абстракции от аппаратного обеспечения (вызовы функций, переменные).
                                                  3. Наличие алгоритмических абстракций (классы).
                                                  4. Скриптовые языки.
                                                    Причём язык относится не к конкретному классу, а к диапазону.

                                                  И, если уж на то пошло, машинный код современных процессоров — тоже высокоуровневый, потому что уже давно не соответствует реальной архитектуре. Команды транслируются во внутренние инструкции, регистры маппятся.

                                                  +4
                                                  Так или иначе, нам по-прежнему нужен язык низкого уровня, причём построенный именно для популярных фоннеймановских компьютеров. И пускай C устарел, но, видимо, любому его преемнику всё равно придётся отталкиваться от тех же самых принципов.

                                                  С последним выводом в статье не согласен. Сделали Rust, который не наследует архаику C и дает все возможности низкоуровневого программирования, но имеет современные возможности
                                                    0

                                                    Да, действительно. И помимо Rust есть множество приличных языков.


                                                    Оригинальная статья сильно критиковала не только сам С как язык, а самую модель программирования, общую для С и Rust, на этой критике я и постарался сконцентрироваться.

                                                      +2
                                                      и дает все возможности низкоуровневого программирования

                                                      Кроме скорости. Те тесты, которые есть на benchmarksgame-team.pages.debian.net/benchmarksgame/fastest/gcc-rust.html показывают просадку.
                                                      Кроме, разумеется, тех тестов, в которых растоманы пошли на уловки — в С-коде используются вложенные циклы, а в Rust — интринсики процессора напрямую.
                                                        +1

                                                        Теоретически говоря Rust (и С++) предоставляют возможности для оптимизации, которых так просят компиляторщики.


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


                                                        https://benchmarksgame-team.pages.debian.net/benchmarksgame/fastest/c.html

                                                          +2

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

                                                            0

                                                            Да, зависит от практики, и это парадокс.


                                                            Говорю ж: нет серьезных причин, по которым С++ должен быть медленней С, особенно в случае GCC/Clang. Вроде как вычисления во время компиляции и шаблоны позволяют, например, не использовать так обильно void* и, скажем, более эффектевно инлайнить код.


                                                            Но практический код на С++ регулярно оказывается медленней. Неплохо об этом не так давно на Хабре писали: https://habr.com/ru/post/347688/

                                                              0
                                                              нет серьезных причин, по которым С++ должен быть медленней С

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

                                                                  ну, последняя языкодельная мода нечто подобное и диктует, когда от полиморфизма через наследование в принципе отказываются.


                                                                  Хотя остаются всякого рода интерфейсоподобные приемы, но все же.

                                                                    +1
                                                                    а вот классы тянут за собой автоматически и RTTI и виртуальные таблицы

                                                                    Только если в них есть виртуальные функции. А полиморфизм можно и без виртуальных функций, ага (CRTP, по факту вирутальные функции на этапе компиляции, вот это всё — попробуйте сделать это с макросами).

                                                                  0
                                                                  Почитайте про Xmacro, используем достаточно часто, когда нужны «template» в С.
                                                                    0

                                                                    ага, я в курсе :-) Вообще, чего только не делают на макросах… Есть еще моя неплохая klib, где популярные структуры данных оформлены так, чтобы указатели не требовались.


                                                                    Но, признаться, макросы — умирающее искусство.

                                                                      0
                                                                      Но, признаться, макросы — умирающее искусство.

                                                                      При этом мало, кто знает, но тьюринг — полное.
                                                                        0

                                                                        Ага, но Тьюринг полнотой обладают даже вычисления на ДНК :-) Все же страшно неудобно, и потом разбираться бывает трудно, чего это случайная запятая вдруг в третьем месте ошибки вызывает. Впрочем, плюсы тут не много лучше.


                                                                        В конечном итоге, ни макросы Си, ни шаблоны плюсов для вычислений не создавались, и это видно :-)


                                                                        Вон, в D все то же самое, но чистенько. Хотя всем все равно :-)

                                                                          +1
                                                                          Все же страшно неудобно, и потом разбираться бывает трудно, чего это случайная запятая вдруг в третьем месте ошибки вызывает.
                                                                          Ага, особенно в таких:
                                                                          #define ADD32(A1F, A1E, A1D, A1C, A1B, A1A, A19, A18, \
                                                                                        A17, A16, A15, A14, A13, A12, A11, A10, \
                                                                                        A0F, A0E, A0D, A0C, A0B, A0A, A09, A08, \
                                                                                        A07, A06, A05, A04, A03, A02, A01, A00, \
                                                                                        B1F, B1E, B1D, B1C, B1B, B1A, B19, B18, \
                                                                                        B17, B16, B15, B14, B13, B12, B11, B10, \
                                                                                        B0F, B0E, B0D, B0C, B0B, B0A, B09, B08, \
                                                                                        B07, B06, B05, B04, B03, B02, B01, B00) \
                                                                                ADDEROUT(A1F, B1F, ADDERCARRY(A1E, B1E, ADDERCARRY(A1D, B1D, ADDERCARRY(A1C, B1C,  \
                                                                              ADDERCARRY(A1B, B1B, ADDERCARRY(A1A, B1A, ADDERCARRY(A19, B19, ADDERCARRY(A18, B18,  \
                                                                              ADDERCARRY(A17, B17, ADDERCARRY(A16, B16, ADDERCARRY(A15, B15, ADDERCARRY(A14, B14,  \
                                                                              ADDERCARRY(A13, B13, ADDERCARRY(A12, B12, ADDERCARRY(A11, B11, ADDERCARRY(A10, B10,  \
                                                                              ADDERCARRY(A0F, B0F, ADDERCARRY(A0E, B0E, ADDERCARRY(A0D, B0D, ADDERCARRY(A0C, B0C,  \
                                                                              ADDERCARRY(A0B, B0B, ADDERCARRY(A0A, B0A, ADDERCARRY(A09, B09, ADDERCARRY(A08, B08,  \
                                                                              ADDERCARRY(A07, B07, ADDERCARRY(A06, B06, ADDERCARRY(A05, B05, ADDERCARRY(A04, B04,  \
                                                                              ADDERCARRY(A03, B03, ADDERCARRY(A02, B02, ADDERCARRY(A01, B01, ADDERCARRY(A00, B00,  \
                                                                              0)))))))))))))))))))))))))))))))),                                                   \
                                                                                                     ADDEROUT(A1E, B1E, ADDERCARRY(A1D, B1D, ADDERCARRY(A1C, B1C,  \
                                                                              ADDERCARRY(A1B, B1B, ADDERCARRY(A1A, B1A, ADDERCARRY(A19, B19, ADDERCARRY(A18, B18,  \
                                                                              ADDERCARRY(A17, B17, ADDERCARRY(A16, B16, ADDERCARRY(A15, B15, ADDERCARRY(A14, B14,  \
                                                                              ADDERCARRY(A13, B13, ADDERCARRY(A12, B12, ADDERCARRY(A11, B11, ADDERCARRY(A10, B10,  \
                                                                              ADDERCARRY(A0F, B0F, ADDERCARRY(A0E, B0E, ADDERCARRY(A0D, B0D, ADDERCARRY(A0C, B0C,  \
                                                                              ADDERCARRY(A0B, B0B, ADDERCARRY(A0A, B0A, ADDERCARRY(A09, B09, ADDERCARRY(A08, B08,  \
                                                                              ADDERCARRY(A07, B07, ADDERCARRY(A06, B06, ADDERCARRY(A05, B05, ADDERCARRY(A04, B04,  \
                                                                              ADDERCARRY(A03, B03, ADDERCARRY(A02, B02, ADDERCARRY(A01, B01, ADDERCARRY(A00, B00,  \
                                                                              0))))))))))))))))))))))))))))))),                                                    \
                                                                                                                          ADDEROUT(A1D, B1D, ADDERCARRY(A1C, B1C,  \
                                                                              ADDERCARRY(A1B, B1B, ADDERCARRY(A1A, B1A, ADDERCARRY(A19, B19, ADDERCARRY(A18, B18,  \
                                                                              ADDERCARRY(A17, B17, ADDERCARRY(A16, B16, ADDERCARRY(A15, B15, ADDERCARRY(A14, B14,  \
                                                                              ADDERCARRY(A13, B13, ADDERCARRY(A12, B12, ADDERCARRY(A11, B11, ADDERCARRY(A10, B10,  \
                                                                              ADDERCARRY(A0F, B0F, ADDERCARRY(A0E, B0E, ADDERCARRY(A0D, B0D, ADDERCARRY(A0C, B0C,  \
                                                                              ADDERCARRY(A0B, B0B, ADDERCARRY(A0A, B0A, ADDERCARRY(A09, B09, ADDERCARRY(A08, B08,  \
                                                                              ADDERCARRY(A07, B07, ADDERCARRY(A06, B06, ADDERCARRY(A05, B05, ADDERCARRY(A04, B04,  \
                                                                              ADDERCARRY(A03, B03, ADDERCARRY(A02, B02, ADDERCARRY(A01, B01, ADDERCARRY(A00, B00,  \
                                                                              0)))))))))))))))))))))))))))))),                                                     \
                                                                                                                                               ADDEROUT(A1C, B1C,  \
                                                                              ADDERCARRY(A1B, B1B, ADDERCARRY(A1A, B1A, ADDERCARRY(A19, B19, ADDERCARRY(A18, B18,  \
                                                                              ADDERCARRY(A17, B17, ADDERCARRY(A16, B16, ADDERCARRY(A15, B15, ADDERCARRY(A14, B14,  \
                                                                              ADDERCARRY(A13, B13, ADDERCARRY(A12, B12, ADDERCARRY(A11, B11, ADDERCARRY(A10, B10,  \
                                                                              ADDERCARRY(A0F, B0F, ADDERCARRY(A0E, B0E, ADDERCARRY(A0D, B0D, ADDERCARRY(A0C, B0C,  \
                                                                              ADDERCARRY(A0B, B0B, ADDERCARRY(A0A, B0A, ADDERCARRY(A09, B09, ADDERCARRY(A08, B08,  \
                                                                              ADDERCARRY(A07, B07, ADDERCARRY(A06, B06, ADDERCARRY(A05, B05, ADDERCARRY(A04, B04,  \
                                                                              ADDERCARRY(A03, B03, ADDERCARRY(A02, B02, ADDERCARRY(A01, B01, ADDERCARRY(A00, B00,  \
                                                                              0))))))))))))))))))))))))))))),                                                      \
                                                                                ADDEROUT(A1B, B1B, ADDERCARRY(A1A, B1A, ADDERCARRY(A19, B19, ADDERCARRY(A18, B18,  \
                                                                              ADDERCARRY(A17, B17, ADDERCARRY(A16, B16, ADDERCARRY(A15, B15, ADDERCARRY(A14, B14,  \
                                                                              ADDERCARRY(A13, B13, ADDERCARRY(A12, B12, ADDERCARRY(A11, B11, ADDERCARRY(A10, B10,  \
                                                                              ADDERCARRY(A0F, B0F, ADDERCARRY(A0E, B0E, ADDERCARRY(A0D, B0D, ADDERCARRY(A0C, B0C,  \
                                                                              ADDERCARRY(A0B, B0B, ADDERCARRY(A0A, B0A, ADDERCARRY(A09, B09, ADDERCARRY(A08, B08,  \
                                                                              ADDERCARRY(A07, B07, ADDERCARRY(A06, B06, ADDERCARRY(A05, B05, ADDERCARRY(A04, B04,  \
                                                                              ADDERCARRY(A03, B03, ADDERCARRY(A02, B02, ADDERCARRY(A01, B01, ADDERCARRY(A00, B00,  \
                                                                              0)))))))))))))))))))))))))))),                                                       \
                                                                                                     ADDEROUT(A1A, B1A, ADDERCARRY(A19, B19, ADDERCARRY(A18, B18,  \
                                                                              ADDERCARRY(A17, B17, ADDERCARRY(A16, B16, ADDERCARRY(A15, B15, ADDERCARRY(A14, B14,  \
                                                                              ADDERCARRY(A13, B13, ADDERCARRY(A12, B12, ADDERCARRY(A11, B11, ADDERCARRY(A10, B10,  \
                                                                              ADDERCARRY(A0F, B0F, ADDERCARRY(A0E, B0E, ADDERCARRY(A0D, B0D, ADDERCARRY(A0C, B0C,  \
                                                                              ADDERCARRY(A0B, B0B, ADDERCARRY(A0A, B0A, ADDERCARRY(A09, B09, ADDERCARRY(A08, B08,  \
                                                                              ADDERCARRY(A07, B07, ADDERCARRY(A06, B06, ADDERCARRY(A05, B05, ADDERCARRY(A04, B04,  \
                                                                              ADDERCARRY(A03, B03, ADDERCARRY(A02, B02, ADDERCARRY(A01, B01, ADDERCARRY(A00, B00,  \
                                                                              0))))))))))))))))))))))))))),                                                        \
                                                                                                                          ADDEROUT(A19, B19, ADDERCARRY(A18, B18,  \
                                                                              ADDERCARRY(A17, B17, ADDERCARRY(A16, B16, ADDERCARRY(A15, B15, ADDERCARRY(A14, B14,  \
                                                                              ADDERCARRY(A13, B13, ADDERCARRY(A12, B12, ADDERCARRY(A11, B11, ADDERCARRY(A10, B10,  \
                                                                              ADDERCARRY(A0F, B0F, ADDERCARRY(A0E, B0E, ADDERCARRY(A0D, B0D, ADDERCARRY(A0C, B0C,  \
                                                                              ADDERCARRY(A0B, B0B, ADDERCARRY(A0A, B0A, ADDERCARRY(A09, B09, ADDERCARRY(A08, B08,  \
                                                                              ADDERCARRY(A07, B07, ADDERCARRY(A06, B06, ADDERCARRY(A05, B05, ADDERCARRY(A04, B04,  \
                                                                              ADDERCARRY(A03, B03, ADDERCARRY(A02, B02, ADDERCARRY(A01, B01, ADDERCARRY(A00, B00,  \
                                                                              0)))))))))))))))))))))))))),                                                         \
                                                                                                                                               ADDEROUT(A18, B18,  \
                                                                              ADDERCARRY(A17, B17, ADDERCARRY(A16, B16, ADDERCARRY(A15, B15, ADDERCARRY(A14, B14,  \
                                                                              ADDERCARRY(A13, B13, ADDERCARRY(A12, B12, ADDERCARRY(A11, B11, ADDERCARRY(A10, B10,  \
                                                                              ADDERCARRY(A0F, B0F, ADDERCARRY(A0E, B0E, ADDERCARRY(A0D, B0D, ADDERCARRY(A0C, B0C,  \
                                                                              ADDERCARRY(A0B, B0B, ADDERCARRY(A0A, B0A, ADDERCARRY(A09, B09, ADDERCARRY(A08, B08,  \
                                                                              ADDERCARRY(A07, B07, ADDERCARRY(A06, B06, ADDERCARRY(A05, B05, ADDERCARRY(A04, B04,  \
                                                                              ADDERCARRY(A03, B03, ADDERCARRY(A02, B02, ADDERCARRY(A01, B01, ADDERCARRY(A00, B00,  \
                                                                              0))))))))))))))))))))))))),                                                          \
                                                                                ADDEROUT(A17, B17, ADDERCARRY(A16, B16, ADDERCARRY(A15, B15, ADDERCARRY(A14, B14,  \
                                                                              ADDERCARRY(A13, B13, ADDERCARRY(A12, B12, ADDERCARRY(A11, B11, ADDERCARRY(A10, B10,  \
                                                                              ADDERCARRY(A0F, B0F, ADDERCARRY(A0E, B0E, ADDERCARRY(A0D, B0D, ADDERCARRY(A0C, B0C,  \
                                                                              ADDERCARRY(A0B, B0B, ADDERCARRY(A0A, B0A, ADDERCARRY(A09, B09, ADDERCARRY(A08, B08,  \
                                                                              ADDERCARRY(A07, B07, ADDERCARRY(A06, B06, ADDERCARRY(A05, B05, ADDERCARRY(A04, B04,  \
                                                                              ADDERCARRY(A03, B03, ADDERCARRY(A02, B02, ADDERCARRY(A01, B01, ADDERCARRY(A00, B00,  \
                                                                              0)))))))))))))))))))))))),                                                           \
                                                                                                     ADDEROUT(A16, B16, ADDERCARRY(A15, B15, ADDERCARRY(A14, B14,  \
                                                                              ADDERCARRY(A13, B13, ADDERCARRY(A12, B12, ADDERCARRY(A11, B11, ADDERCARRY(A10, B10,  \
                                                                              ADDERCARRY(A0F, B0F, ADDERCARRY(A0E, B0E, ADDERCARRY(A0D, B0D, ADDERCARRY(A0C, B0C,  \
                                                                              ADDERCARRY(A0B, B0B, ADDERCARRY(A0A, B0A, ADDERCARRY(A09, B09, ADDERCARRY(A08, B08,  \
                                                                              ADDERCARRY(A07, B07, ADDERCARRY(A06, B06, ADDERCARRY(A05, B05, ADDERCARRY(A04, B04,  \
                                                                              ADDERCARRY(A03, B03, ADDERCARRY(A02, B02, ADDERCARRY(A01, B01, ADDERCARRY(A00, B00,  \
                                                                              0))))))))))))))))))))))),                                                            \
                                                                                                                          ADDEROUT(A15, B15, ADDERCARRY(A14, B14,  \
                                                                              ADDERCARRY(A13, B13, ADDERCARRY(A12, B12, ADDERCARRY(A11, B11, ADDERCARRY(A10, B10,  \
                                                                              ADDERCARRY(A0F, B0F, ADDERCARRY(A0E, B0E, ADDERCARRY(A0D, B0D, ADDERCARRY(A0C, B0C,  \
                                                                              ADDERCARRY(A0B, B0B, ADDERCARRY(A0A, B0A, ADDERCARRY(A09, B09, ADDERCARRY(A08, B08,  \
                                                                              ADDERCARRY(A07, B07, ADDERCARRY(A06, B06, ADDERCARRY(A05, B05, ADDERCARRY(A04, B04,  \
                                                                              ADDERCARRY(A03, B03, ADDERCARRY(A02, B02, ADDERCARRY(A01, B01, ADDERCARRY(A00, B00,  \
                                                                              0)))))))))))))))))))))),                                                             \
                                                                                                                                               ADDEROUT(A14, B14,  \
                                                                              ADDERCARRY(A13, B13, ADDERCARRY(A12, B12, ADDERCARRY(A11, B11, ADDERCARRY(A10, B10,  \
                                                                              ADDERCARRY(A0F, B0F, ADDERCARRY(A0E, B0E, ADDERCARRY(A0D, B0D, ADDERCARRY(A0C, B0C,  \
                                                                              ADDERCARRY(A0B, B0B, ADDERCARRY(A0A, B0A, ADDERCARRY(A09, B09, ADDERCARRY(A08, B08,  \
                                                                              ADDERCARRY(A07, B07, ADDERCARRY(A06, B06, ADDERCARRY(A05, B05, ADDERCARRY(A04, B04,  \
                                                                              ADDERCARRY(A03, B03, ADDERCARRY(A02, B02, ADDERCARRY(A01, B01, ADDERCARRY(A00, B00,  \
                                                                              0))))))))))))))))))))),                                                              \
                                                                                ADDEROUT(A13, B13, ADDERCARRY(A12, B12, ADDERCARRY(A11, B11, ADDERCARRY(A10, B10,  \
                                                                              ADDERCARRY(A0F, B0F, ADDERCARRY(A0E, B0E, ADDERCARRY(A0D, B0D, ADDERCARRY(A0C, B0C,  \
                                                                              ADDERCARRY(A0B, B0B, ADDERCARRY(A0A, B0A, ADDERCARRY(A09, B09, ADDERCARRY(A08, B08,  \
                                                                              ADDERCARRY(A07, B07, ADDERCARRY(A06, B06, ADDERCARRY(A05, B05, ADDERCARRY(A04, B04,  \
                                                                              ADDERCARRY(A03, B03, ADDERCARRY(A02, B02, ADDERCARRY(A01, B01, ADDERCARRY(A00, B00,  \
                                                                              0)))))))))))))))))))),                                                               \
                                                                                                     ADDEROUT(A12, B12, ADDERCARRY(A11, B11, ADDERCARRY(A10, B10,  \
                                                                              ADDERCARRY(A0F, B0F, ADDERCARRY(A0E, B0E, ADDERCARRY(A0D, B0D, ADDERCARRY(A0C, B0C,  \
                                                                              ADDERCARRY(A0B, B0B, ADDERCARRY(A0A, B0A, ADDERCARRY(A09, B09, ADDERCARRY(A08, B08,  \
                                                                              ADDERCARRY(A07, B07, ADDERCARRY(A06, B06, ADDERCARRY(A05, B05, ADDERCARRY(A04, B04,  \
                                                                              ADDERCARRY(A03, B03, ADDERCARRY(A02, B02, ADDERCARRY(A01, B01, ADDERCARRY(A00, B00,  \
                                                                              0))))))))))))))))))),                                                                \
                                                                                                                          ADDEROUT(A11, B11, ADDERCARRY(A10, B10,  \
                                                                              ADDERCARRY(A0F, B0F, ADDERCARRY(A0E, B0E, ADDERCARRY(A0D, B0D, ADDERCARRY(A0C, B0C,  \
                                                                              ADDERCARRY(A0B, B0B, ADDERCARRY(A0A, B0A, ADDERCARRY(A09, B09, ADDERCARRY(A08, B08,  \
                                                                              ADDERCARRY(A07, B07, ADDERCARRY(A06, B06, ADDERCARRY(A05, B05, ADDERCARRY(A04, B04,  \
                                                                              ADDERCARRY(A03, B03, ADDERCARRY(A02, B02, ADDERCARRY(A01, B01, ADDERCARRY(A00, B00,  \
                                                                              0)))))))))))))))))),                                                                 \
                                                                                                                                               ADDEROUT(A10, B10,  \
                                                                              ADDERCARRY(A0F, B0F, ADDERCARRY(A0E, B0E, ADDERCARRY(A0D, B0D, ADDERCARRY(A0C, B0C,  \
                                                                              ADDERCARRY(A0B, B0B, ADDERCARRY(A0A, B0A, ADDERCARRY(A09, B09, ADDERCARRY(A08, B08,  \
                                                                              ADDERCARRY(A07, B07, ADDERCARRY(A06, B06, ADDERCARRY(A05, B05, ADDERCARRY(A04, B04,  \
                                                                              ADDERCARRY(A03, B03, ADDERCARRY(A02, B02, ADDERCARRY(A01, B01, ADDERCARRY(A00, B00,  \
                                                                              0))))))))))))))))),                                                                  \
                                                                                ADDEROUT(A0F, B0F, ADDERCARRY(A0E, B0E, ADDERCARRY(A0D, B0D, ADDERCARRY(A0C, B0C,  \
                                                                              ADDERCARRY(A0B, B0B, ADDERCARRY(A0A, B0A, ADDERCARRY(A09, B09, ADDERCARRY(A08, B08,  \
                                                                              ADDERCARRY(A07, B07, ADDERCARRY(A06, B06, ADDERCARRY(A05, B05, ADDERCARRY(A04, B04,  \
                                                                              ADDERCARRY(A03, B03, ADDERCARRY(A02, B02, ADDERCARRY(A01, B01, ADDERCARRY(A00, B00,  \
                                                                              0)))))))))))))))),                                                                   \
                                                                                                     ADDEROUT(A0E, B0E, ADDERCARRY(A0D, B0D, ADDERCARRY(A0C, B0C,  \
                                                                              ADDERCARRY(A0B, B0B, ADDERCARRY(A0A, B0A, ADDERCARRY(A09, B09, ADDERCARRY(A08, B08,  \
                                                                              ADDERCARRY(A07, B07, ADDERCARRY(A06, B06, ADDERCARRY(A05, B05, ADDERCARRY(A04, B04,  \
                                                                              ADDERCARRY(A03, B03, ADDERCARRY(A02, B02, ADDERCARRY(A01, B01, ADDERCARRY(A00, B00,  \
                                                                              0))))))))))))))),                                                                    \
                                                                                                                          ADDEROUT(A0D, B0D, ADDERCARRY(A0C, B0C,  \
                                                                              ADDERCARRY(A0B, B0B, ADDERCARRY(A0A, B0A, ADDERCARRY(A09, B09, ADDERCARRY(A08, B08,  \
                                                                              ADDERCARRY(A07, B07, ADDERCARRY(A06, B06, ADDERCARRY(A05, B05, ADDERCARRY(A04, B04,  \
                                                                              ADDERCARRY(A03, B03, ADDERCARRY(A02, B02, ADDERCARRY(A01, B01, ADDERCARRY(A00, B00,  \
                                                                              0)))))))))))))),                                                                     \
                                                                                                                                               ADDEROUT(A0C, B0C,  \
                                                                              ADDERCARRY(A0B, B0B, ADDERCARRY(A0A, B0A, ADDERCARRY(A09, B09, ADDERCARRY(A08, B08,  \
                                                                              ADDERCARRY(A07, B07, ADDERCARRY(A06, B06, ADDERCARRY(A05, B05, ADDERCARRY(A04, B04,  \
                                                                              ADDERCARRY(A03, B03, ADDERCARRY(A02, B02, ADDERCARRY(A01, B01, ADDERCARRY(A00, B00,  \
                                                                              0))))))))))))),                                                                      \
                                                                                ADDEROUT(A0B, B0B, ADDERCARRY(A0A, B0A, ADDERCARRY(A09, B09, ADDERCARRY(A08, B08,  \
                                                                              ADDERCARRY(A07, B07, ADDERCARRY(A06, B06, ADDERCARRY(A05, B05, ADDERCARRY(A04, B04,  \
                                                                              ADDERCARRY(A03, B03, ADDERCARRY(A02, B02, ADDERCARRY(A01, B01, ADDERCARRY(A00, B00,  \
                                                                              0)))))))))))),                                                                       \
                                                                                                     ADDEROUT(A0A, B0A, ADDERCARRY(A09, B09, ADDERCARRY(A08, B08,  \
                                                                              ADDERCARRY(A07, B07, ADDERCARRY(A06, B06, ADDERCARRY(A05, B05, ADDERCARRY(A04, B04,  \
                                                                              ADDERCARRY(A03, B03, ADDERCARRY(A02, B02, ADDERCARRY(A01, B01, ADDERCARRY(A00, B00,  \
                                                                              0))))))))))),                                                                        \
                                                                                                                          ADDEROUT(A09, B09, ADDERCARRY(A08, B08,  \
                                                                              ADDERCARRY(A07, B07, ADDERCARRY(A06, B06, ADDERCARRY(A05, B05, ADDERCARRY(A04, B04,  \
                                                                              ADDERCARRY(A03, B03, ADDERCARRY(A02, B02, ADDERCARRY(A01, B01, ADDERCARRY(A00, B00,  \
                                                                              0)))))))))),                                                                         \
                                                                                                                                               ADDEROUT(A08, B08,  \
                                                                              ADDERCARRY(A07, B07, ADDERCARRY(A06, B06, ADDERCARRY(A05, B05, ADDERCARRY(A04, B04,  \
                                                                              ADDERCARRY(A03, B03, ADDERCARRY(A02, B02, ADDERCARRY(A01, B01, ADDERCARRY(A00, B00,  \
                                                                              0))))))))),                                                                          \
                                                                                ADDEROUT(A07, B07, ADDERCARRY(A06, B06, ADDERCARRY(A05, B05, ADDERCARRY(A04, B04,  \
                                                                              ADDERCARRY(A03, B03, ADDERCARRY(A02, B02, ADDERCARRY(A01, B01, ADDERCARRY(A00, B00,  \
                                                                              0)))))))),                                                                           \
                                                                                                     ADDEROUT(A06, B06, ADDERCARRY(A05, B05, ADDERCARRY(A04, B04,  \
                                                                              ADDERCARRY(A03, B03, ADDERCARRY(A02, B02, ADDERCARRY(A01, B01, ADDERCARRY(A00, B00,  \
                                                                              0))))))),                                                                            \
                                                                                                                          ADDEROUT(A05, B05, ADDERCARRY(A04, B04,  \
                                                                              ADDERCARRY(A03, B03, ADDERCARRY(A02, B02, ADDERCARRY(A01, B01, ADDERCARRY(A00, B00,  \
                                                                              0)))))),                                                                             \
                                                                                                                                               ADDEROUT(A04, B04,  \
                                                                              ADDERCARRY(A03, B03, ADDERCARRY(A02, B02, ADDERCARRY(A01, B01, ADDERCARRY(A00, B00,  \
                                                                              0))))),                                                                              \
                                                                                ADDEROUT(A03, B03, ADDERCARRY(A02, B02, ADDERCARRY(A01, B01, ADDERCARRY(A00, B00,  \
                                                                              0)))),                                                                               \
                                                                                                     ADDEROUT(A02, B02, ADDERCARRY(A01, B01, ADDERCARRY(A00, B00,  \
                                                                              0))),                                                                                \
                                                                                                                          ADDEROUT(A01, B01, ADDERCARRY(A00, B00,  \
                                                                              0)),                                                                                 \
                                                                                                                                               ADDEROUT(A00, B00, 0)


                                                                          На мой замутненный взгляд самое отвратительно в темлейтах с++ — это кодогенерация реальных функций в количестве соответствующем использованным типам. Т.е. делаю я какой-нибудь темплейт на два аргумента, использую с разными типами 50 раз в коде и у меня сгенерируется 50 функций. Поэтому темплейты в каком-нибудь ограниченном пространстве типа встраиваемых систем табу.
                                                                            0

                                                                            не, ну ваш пример еще очень даже чистый. :-) Это не ассемблер ли на макросах? :-)

                                                                              0
                                                                              Почти. В свое время начинал писать книгу по использованию макросов, плавно доказывая его Тьюринг полноту и создание на нем полноценного математического процессора.
                                                                              А потом сменил работу и времени стало критически нехватать.
                                                                                0

                                                                                а что за книжка?

                                                                                  +1
                                                                                  начинал

                                                                                  Не закончил.
                                                                                  Обрывками сейчас валяется все, когда-нибудь соберу в кучу
                                                                                  Клицк хере
                                                                                  image
                                                                                    +1

                                                                                    не поделитесь набросками? :-) Интересно, такой литературы почти уже и не пишут.

                                                                                      0
                                                                                      Там все очень недоработано, я все же надеюсь вернуться к идее через некоторое время. Так что сорян, не хочу делиться некачественным товаром.
                                                                                      +1
                                                                                      Просто отмечу, что очень забавно, что небольшую картинку вы под спойлер спрятали, а простыню текста на несколько экранов — нет
                                                                                +1
                                                                                • У вас 50 разных типов?
                                                                                • Если эти функции написать ручками, их будет не 50?
                                                                                  –1
                                                                                  У вас 50 разных типов?

                                                                                  50 разных сочетаний можно добиться всего 7-8 типами, если на вход в темплейт идут два параметра. Какой-нибудь примитивный compare, типа такого:
                                                                                  template <class T> struct equal_to : binary_function <T,T,bool> {
                                                                                    bool operator() (const T& x, const T& y) const {return x==y;}
                                                                                  };

                                                                                  Начнем сравнивать double, int, uint, void *, bool и прочие штуки между собой и получим генерацию новой функции на каждое новое сочетание.

                                                                                  Если входящих аргументов будет 3, то при 7 базовых типах это будет 343 сгенерированные функции

                                                                                  Если эти функции написать ручками, их будет не 50?

                                                                                  
                                                                                  #define COMPARE(x, y) (x == y)
                                                                                  

                                                                                    0
                                                                                    Шаблоны вида return x == y любой адекватный компилятор встроит и драгоценное место они занимать не будут.

                                                                                    А конкретно в вашем примере (const T& x, const T& y) комбинаторного взрыва не будет. Думаю, очевидно, почему.
                                                                              0

                                                                              Теория рекурсивных функций — не самая сильная моя часть в матлогике, но вроде как μ-рекурсивные функции в препроцессоре задать нельзя, что делает его неполным по Тьюрингу.

                                                                                0
                                                                                Полнота по Тьюрингу — характеристика исполнителя (множества вычисляющих элементов) в теории вычислимости, означающая возможность реализовать на нём любую вычислимую функцию.

                                                                                На препроцессоре реализуются математические операции, из которых в комбинациях создаются функции обработки входных данных. Единственный минус мне известный и не влияющий на полноту — нельзя задать константы как например 0x12345678, только как (12, 34, 56, 78) (точнее можно, но займет около 100 ГБ на реализацию)
                                                                                  0

                                                                                  Поэтому я и сказал про μ-рекурсивные функции.


                                                                                  Я могу реализовать натуральные числа и математические операции на System F, но System F не является тьюринг-полной.

                                                                            0

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

                                                                          0
                                                                          Еще бы эти тесты честно писали, а то в одном тесте в С используют VLA, в С++ коде std::vector
                                                                          0
                                                                          Если бы еще эти тесты говорили о чем-то кроме немного разной оптимизации GCC и Clang под расширения наборов инструкций x86, было бы совсем хорошо.
                                                                          +5
                                                                          там, где rust «дает все возможности низкоуровневого программирования», он теряет все свои преимущества.
                                                                            0

                                                                            Интересно. Куда же девается система типов, возможности для разграничения потенциально опасного и безопасного кода, дженерики и т.п.?

                                                                              +4
                                                                              там, где начинается низкоуровневое программирование, начинается unsafe. А там, где начинается unsafe, теряется киллер фича раста — гарантия безопасности. Остальные фичи раста не уникальны
                                                                                +2

                                                                                Аналоги borrow checker есть в Dyon (скриптовый), MLKit (функциональный), Carp (вариант Lisp'а), LinearML (функциональный язык с потенциалом системного использования, в разработке, последний коммит 5 лет назад), Cyclone (safe C), Discus (функциональный), Cone (системный язык, в разработке, последний коммит 4 месяца назад), Rust.


                                                                                Типы суммы, pattern matching, дженерики/темплейты в потенциально системных языках: Cone, Rust.


                                                                                В общем — да, не уникально.

                                                                                  0

                                                                                  Ох, каждый раз когда вижу упоминания компиляторов Standard ML — грустно становится. Такой красивый язык, такие шикарные компиляторы… И из всего семейства более-менее популярным стали только ленивый Haskell и некрасивый Ocaml.


                                                                                  Вкусовщина, конечно, спорить тут не буду :-)

                                                                                    0

                                                                                    Так лень — это ж хорошо. Как раз вчера наваял очередную парсилку логов, прожевавшую 10 гигов и посчитавшую все нужные мне статистики, сожрав 8 мегабайт памяти. И даже думать не надо, чтобы так получилось, просто пишешь «прочитать файл, разбить на строчки, применить парсер к каждой строчке, свернуть с такой-то функцией».


                                                                                    Добавят foldMap' в base — вообще шикарно будет.

                                                                                      0

                                                                                      уф… это ж такой спор можно развести… :-) А тут и так флейма...


                                                                                      В общем, я за предсказуемость eager evaluation, тем более, что ленивые вычисления на Standard ML или Окамле очень даже делаются.

                                                                                        +1

                                                                                        Из лени очень хороший клей между модулями получается, лень помогает писать композабельный код. Больше не нужны никакие итераторы, генераторы, yieldы всякие, просто производишь ленивый список (или дерево, или ещё что), и всё. Это отделяет структуру данных от стратегии её порождения. Да и узёл завязать с ленью можно и построить двусвязный список или какое-нибудь хитрое дерево, где у каждого узла есть ссылка и на родителя, и на детей, без какой-либо мутабельности.


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


                                                                                        Но при прочих равных space leaks — это неприятно, да, пользоваться ленью надо уметь. Поэтому в том же хаскеле есть {-# LANGUAGE Strict #-} и более лайтовая {-# LANGUAGE StrictData #-}, ну и уже очень давно есть bang patterns. Так что вполне легко добиться строгости и в нём, если она вдруг нужна.

                                                                                          0

                                                                                          Я все это знаю, Окасаки проделал потрясающую работу и вообще большой молодец, но… У него простейшие структуры данных делаются весьма нетривиально, если с чисто практической точки зрения.


                                                                                          Ну а тот факт, что для околоМЛ языков легче теоремы доказывать я никому никогда не продам :-)


                                                                                          Вы можете меня к стенке поставить и расстрелять, но я за предсказуемость и простоту :-)

                                                                          +4
                                                                          Контора пишет — крестьянин пашет ©.
                                                                            +1
                                                                            Задержки при получении инструкциями данных из памяти частично компенсируются внеочередным выполнением (англ. instruction-level parallelism)

                                                                            Строго говоря, в ссылку надо ставить статью про out-of-order, раз уж по-русски написано именно про это. Хотя бы потому, что параллелизм уровня инструкций может быть реализован и на in-order процессорах с явной параллельностью (здравствуй, VLIW). Вдобавок, про параллелизм уровня инструкций неявно уже сказано в пункте про суперскалярность, которая как раз ILP и реализует.
                                                                              0

                                                                              Да, можно было бы тут раскрыть получше.


                                                                              Я когда думал над этими абзацами все время сталкивался с тем, что кратко изложить все эти тесносвязанные понятия сложно. Надо начинать с какой-нибудь простой модели, а потом вносить в нее оптимизации и раскрывать их суть и то, как они взаимодействуют… Материала на приличного размера книгу хватит :-)


                                                                              Мораль тут в любом случае простая: это неизбежные оптимизации, применяемые во всех современных производительных процессорах общего назначения.

                                                                                0
                                                                                Материала на приличного размера книгу хватит :-)

                                                                                Это даа...:)

                                                                                Мораль тут в любом случае простая: это неизбежные оптимизации, применяемые во всех современных производительных процессорах общего назначения.

                                                                                А вот тут опять неточность. Большинство VLIW-процессоров не включают out-of-order, но некоторые из них являются современными, производительными процессорами общего назначения. Так что с формальной точки зрения ИМХО стоило бы написать «оптимизации, применяемые в большинстве… процессоров ...».
                                                                                Хотя про ILP и branch predictor согласен: без них сейчас вообще никак.
                                                                                  0

                                                                                  а я там везде пишу "популярные процессоры общего назначения".


                                                                                  VLIW же не особо пока взлетели, не так ли? :-)

                                                                                    0
                                                                                    а я там везде пишу «популярные процессоры общего назначения».


                                                                                    В статье по большей части да. Но в Вашем сообщении, на которое я и отвечал, ни слова о популярности не было. Это во-первых.
                                                                                    Во-вторых, архитектуры VLIW есть и общего назначения: Эльбрус и Itanium как минимум. Так что утверждение в Вашем сообщении я посчитал некорректным, о чём и написал.

                                                                                    Впрочем, есть вероятность, что мы с Вами понимаем слова «процессор общего назначения» по-разному. Я под этими словами понимаю синоним CPU в том смысле, что есть также и специализированные processing units: GPU, TPU и тому подобные.
                                                                                      0

                                                                                      Я совершенно с вами согласен: такие архитектуры были, есть и используются. Но не слишком широко, если сравнивать с традиционными RISC/CISC гибридами (тут же мы не будем спорить? :-) ).


                                                                                      Просто если упоминать VLIW, то придется еще обсуждать весь зоопарк опробованных в роли ЦПУ архитектур, и это уже слишком. :-)

                                                                                        0
                                                                                        тут же мы не будем спорить? :-)

                                                                                        Ага, тут мы не будем спорить:)

                                                                                        Просто если упоминать VLIW, то придется еще обсуждать весь зоопарк опробованных в роли ЦПУ архитектур, и это уже слишком. :-)

                                                                                        ИМХО нужно просто оставить точными формулировки. А это можно сделать, и не упоминая VLIW.
                                                                              0
                                                                              Никто не станет спорить с тем, что язык ассемблера находится на самом низком уровне.

                                                                              Если быть точным, то по эту сторону кристалла самый низкий — это язык машинных кодов. Ну, если брать ГОСТовскую терминологию; язык ассемблера — ака язык мнемокодов — на ступень выше.

                                                                              VlK
                                                                              я не имею в виду «ГОСТ на терминологию», я имею в виду «типичную терминологию, используемую в отраслевых ГОСТах». Сейчас уже не вспомню точно где, но год или два назад, когда в предыдущий раз на хабре были холиворы касательно того, что такое низкий уровень, что такое высокий, что такое транслятор и что такое компилятор и т.п. — нашел это.

                                                                              VlK
                                                                              а, нет, вру. Все же это были разные стандарты, втч ГОСТ на терминологию.
                                                                              ГОСТ 19781-90. Обеспечение систем обработки информации программное. Термины и определения


                                                                              Обратите внимание, что там вводится понятие автокода, который занимает промежуточное положение между машинным языком и ассемблером.
                                                                                0

                                                                                А что это за ГОСТ такой? Я не знал, что где-то вообще можно найти строгие определения.

                                                                                  0

                                                                                  Интересный, кстати, документ!


                                                                                  Но там "языки низкого уровня" вообще не упоминаются! Имеются только машинный код, автокод, языки ассемблера и языки высокого уровня.

                                                                                  0
                                                                                  Никто не станет спорить с тем, что язык ассемблера находится на самом низком уровне.


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

                                                                                  А язык Форд по вашему мнению выше или ниже уровня С?
                                                                                    0

                                                                                    "Форд" или все же "Форт"? :-)

                                                                                      0
                                                                                      Проклятая автокоррекция… :-\
                                                                                        0

                                                                                        Но вообще вопрос хороший.


                                                                                        Если в контексте статьи, то вполне себе удовлетворяет требованиям: легко портируется, есть стандарт, детали платформ различает (в определенных рамках). Хотя я не спец по Форту, признаться.


                                                                                        Вы как думаете?

                                                                                          0
                                                                                          Сложный вопрос, поэтому и спрашиваю… ;-)

                                                                                          Басовые идеи Форта и отличия от С, по моему мнению, следующие:

                                                                                          А) Форт абстрагирует стековую машину, а не регистровую PDP-11 как С

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

                                                                                          В) Большинство реализаций Форта — интерпретаторы, а не компиляторы, хотя компилируемый Форт наверняка возможен (я не сталкивался). А вот об интерпретаторах С я не слышал

                                                                                          Г) Наконец, Форт — это безтиповой язык, а вот С хотя и близок к безтипности, но всё-таки официально таковым не считается

                                                                                          Так что решайте сами…

                                                                                          Кстати, вопрос о наличии типов в языке — основной с точки зрения различия «высоких» и «низких» языков
                                                                                            0
                                                                                            Кстати, вопрос о наличии типов в языке — основной с точки зрения различия «высоких» и «низких» языков

                                                                                            Что думаете о TAL-0 assembly?

                                                                                              –1
                                                                                              интерпретаторах С я не слышал

                                                                                              ru.wikipedia.org/wiki/Tiny_C_Compiler
                                                                                                0
                                                                                                Это компилятор, просто он может сразу запустить скомпилированный код и внешне подобен интерпретатору.

                                                                                                Например есть церновский CINT или пришедший к нему на замену Cling (но это уже вроде как C++).
                                                                                                0
                                                                                                Большинство реализаций Форта — интерпретаторы, а не компиляторы

                                                                                                Форт одновременно является и тем и другим.
                                                                                                Например
                                                                                                \ тут форт интерпретируемый
                                                                                                S" Hello from interpreting mode!" TYPE CR
                                                                                                : hello \ а тут уже компилируемый, : нас перевело в этот режим
                                                                                                    S" Hello from compiling mode"  \ но S" является immediate-словом и поэтому
                                                                                                                                   \ не скомпилировалось, а было вызвано, оно
                                                                                                                                   \ распарсило строковый литерал, где-то его
                                                                                                                                   \ сохранило и скомпилировало код, который
                                                                                                                                   \ заносит на стек адрес строки и ее длину
                                                                                                    TYPE CR  \ это обычные слова, они просто скомпилировались
                                                                                                ;  \ ; тоже immediate-слово, заканчивает определение и переводит нас обратно
                                                                                                   \ в режим интерпретации
                                                                                                
                                                                                                \ мы снова в интерпретаторе
                                                                                                hello  \ зовем только что скомпилированное слово
                                                                                                
                                                                                                BYE  \ пока ;)
                                                                                                

                                                                                                  0

                                                                                                  Большинство реализаций Форта — интерпретаторы, а не компиляторы, хотя компилируемый Форт наверняка возможен (я не сталкивался). А вот об интерпретаторах С я не слышал


                                                                                                  в случае использования прямого шитого кода получается де факто компилятор.
                                                                                                    0
                                                                                                    А при использовании косвенного или подпрограммного не получается чтоли? Или например байткода? Хотите сказать там ничего не компилируется?

                                                                                                    К тому же есть системы, компилирующиеся в нативный код: SP-Forth, SwiftForth
                                                                                                      0

                                                                                                      да получается, конечно. Именно при использовании подпрограммного. Про прямой я ошибся, но с мобильника уже не мог поправить.

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

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

                                                                                                        Большая разница с компиляторами большинства других языков в том, что этот код компилируется в оперативной памяти и часто там и остается, если в программе явно не было указано команды сохранения в файл. Чтобы было понятнее, можно считать, что это аналог запуска Си программы командой tcc -run program.c.

                                                                                                        P.S. Да и, кстати, настоящих интерпретируемых языков осталось немного, почти всегда это компиляторы в байткод, которые (если явно не было указано другого) сразу запускают интерпретатор этого байткода, да и там тоже часто JIT-компилятор.
                                                                                                    0

                                                                                                    Ну, вот встречные соображения.


                                                                                                    Буду честнен, простой Форт на шитом коде я делал в качестве упражнения, и прикладной код на нем не писал. Поправьте, если мой ограниченный опыт ложен.


                                                                                                    а) в PDP-11 и x86 вполне себе есть как стек, так и регистры. Использование этого стека, в сущности, вопрос реализации как в С, так и в Форте.


                                                                                                    б) да


                                                                                                    в) делают и интерпретаторы С, например, для проверки работы программ на предмет всяких там неопределенных поведений, хотя, конечно, практически ими никто не пользуется


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


                                                                                                    Последней же вопрос. Типы в форме Си, где они привязаны к платформе, разве не достаточно низкий уровень? Железные вопросы они отражают, это ж не Haskell.

                                                                                                      +1

                                                                                                      Стековая машина предполагает не просто наличие стека, а расположение операндов в стеке. Как в fpu.

                                                                                                        0

                                                                                                        вы про абстрактную стековую машину? Это ж просто модель.


                                                                                                        Реальные популярные наборы инструкций — страшные химеры в этом смысле, особенно x86. Что-то так, через стек, что-то эдак, только с регистрами...

                                                                                                        +1
                                                                                                        Хочу лишь немного уточнить свои мысли по Форту и С:

                                                                                                        а) Без сомнений — любая мало-мальски универсальная регистровая машина (PDP-11, x86) может работать в качестве или эмулировать стековую (так делаются в большинстве реализаций Форта). Моя мысль была в том что Форту требуется стековая виртуальная машина, поскольку всё взаимодействие в словаре происходит через стек. Если убрать стек и оставить словарь, то Форта — как мы его знаем — не будет. А вот С семантически от наличия или отсутствия стека не зависит, хотя все его реализации о которых я знаю безусловно стек используют.

                                                                                                        б) да, да :)

                                                                                                        в) Не представляю каким образом система типов в С будет работать в режиме интерпретирования без жесткой run-time поддержки (тут мы уже движемся в сторону Питона), хотя наверно всё возможно при желании… Система типов в С слабая, особенно в ранних стандартах, но всё же компилятор на неё опирается.

                                                                                                        г) Тут опять-таки я имел в виду, что работа ядра Форта (стек + словарь) никак не зависит от того что содержится в том или ином машинном слове (или в терминологии Форта — ячейке). Семантика того что содержат отдельные ячейки — дело самой программы. А вот работа компилятора С от типов очень даже зависит, например (и особенно) в части приведения к типам в выражениях.

                                                                                                        Последний вопрос: Конечно, система типов в С базируется на «низких» машинных типах, отсюда и утверждения о том что С — это glorified assembler. И в этом сила С как системного языка. Но всё же там есть и более «высокие» т.е. производные типы — структуры и массивы, хотя их реализация очень проста и максимально приближена к машинному уровню.
                                                                                                          0

                                                                                                          Все так :-)


                                                                                                          Только как-то мне кажется, что в Си неявным образом все равно требуется стек для некоторых аспектов функций

                                                                                              0
                                                                                              Си — высокоуровневый язык программирования. См. Ассемблер.
                                                                                                0

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

                                                                                                  0

                                                                                                  Очень просто, если язык позволяет использовать конкретную команду процессора это язык низкого уровня.

                                                                                                    +1

                                                                                                    При таком определении языком низкого уровня является любой язык, поддерживающий ассемблерные вставки, например, Object Pascal (Delphi) или С++.
                                                                                                    Не могу принять такое определение.

                                                                                                      0

                                                                                                      Только хотел написать то же самое :-)


                                                                                                      На Си, например, вполне себе используют векторные встроенные функции (intrinsic), которые один к одному соответствуют векторным инструкциям процессорам.

                                                                                                        0

                                                                                                        Ассемблерные вставки потому и называются вставками, что не входят в сам язык.

                                                                                                          0
                                                                                                          В Delphi ещё GOTO есть. Это не вставка, а часть языка. Считается за «процессорную команду»?
                                                                                                            0
                                                                                                            Нет, потому что вы не можете использовать адрес или регистр в goto; у процессора нет концепции метки, процессорная команда безусловного перехода использует адрес или регистр.
                                                                                                  0

                                                                                                  C это, безусловно, язык высокого уровня. Ассемблер — низкого. Даже Форт можно считать языком низкого уровня только если он работает на форт-процессоре (таких хватает), хотя, безотносительно, у него куда как больше оснований чем у С чтобы на это претендовать.

                                                                                                    0

                                                                                                    почему это безусловно-то? определение тогда дадите?


                                                                                                    Мои вот требования плюс-минус соответствуют требованиям разработчиков системного софта: ОС, драйверов и т.д.

                                                                                                      0

                                                                                                      Но ведь вы сами в своей статье не приводите определений! Вы не пишите, что считаете высокооуровневым языком, а что низкоуровневым. Мы можем только догадываться, что под низкоуровневым вы понимаете компилируемые неуправляемве языки без механизмов обеспечивающих безопасность использования памяти. Тогда сюда вроде как и упомянутый Delphi подходит и Fortran и бог знает кто ещё.

                                                                                                        –1

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


                                                                                                        Мои вот требования

                                                                                                        Ваши требования не отменяют необходимости понимания базовых вещей.
                                                                                                        Я бы порекомендовал автору, если не пописать в/на машинных кодах / Ассемблере / Форте / С, то, хотя бы, почитать побольше для понимания проблематики.

                                                                                                          +1

                                                                                                          "очевидно потому что очевидно"


                                                                                                          "вы, незнакомый мне человек, не писали на ассемблере, форте, с и в машинных кодах"


                                                                                                          Аж даже не знаю, с чего начать конструктивную дискуссию. :-)

                                                                                                            –2

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

                                                                                                      +3

                                                                                                      Вообще не очень понятно это желание некоторых отнести Си к языкам низкого уровня, а ещё больше удивляет с каким упорством эти люди пытаются отстоять эту точку зрения. Зачем это все? Чтобы быть "крутым" программистом пишущим на низкоуровневом языке? Так на это всем, кроме сишников желающих относится к "низкоуровневом" вообще наплевать — они либо пишут на высокоуровневых и не парятся, либо, если хотят основательно выпендриться на эту тему, пишут на чем-то более низкоуровневом. И вообще, надо понимать, что никакой особой крутости в том на чем писать нет, крутость она в том что конкретно ты на этом напишешь.

                                                                                                        +3

                                                                                                        "Да не пригорает у меня!!!111" ©

                                                                                                        +4
                                                                                                        Зачем все эти споры, низкого или высокого уровня? Раз с одного конца он смотрится эдак, а с другого наоборот, совершенно логично из этого вытекает, что Си — язык СРЕДНЕГО уровня.
                                                                                                          0
                                                                                                          И все таки язык Си — ВЫШЕ СРЕДНЕГО уровня, так как позволяет программисту создавать новые конструкции и абстрактные сущности, кторыми он в последствии оперирует для решения задачи. И не забывайте про такое мощное средство как препроцессор! Или это уже не Си? ;-)
                                                                                                            +3
                                                                                                            позволяет программисту создавать новые конструкции и абстрактные сущности

                                                                                                            Это какие же? Новую функцию или структуру?

                                                                                                            не забывайте про такое мощное средство как препроцессор!

                                                                                                            Стандартный сишный препроцессор очень убог и примитивен. Тупая подстановка и пара операций. Сравните с m4 или макросами в лиспе, которые являются полноценными тьюринг-полными языками.
                                                                                                            Заголовок спойлера
                                                                                                            Единственные реально расширяемые языки — это форт и его потомки (например более высокоуровневый Factor или недавно представленный fift для TON). Форт одновременно является и препроцессором, и интерпретатором, и компилятором, и линкером, и операционной системой и вообще чем угодно. Например в форте нет никакого синтаксиса для описания классов, пишем небольшое расширение и тут же можем его использовать. Абсолютно любое слово можно переопределить как угодно прямо во время работы транслятора. Можно ввести в язык любую синтаксическую конструкцию, какая только взбредет в голову. В добавок механизм словарей (в других языках наиболее близким понятием является неймспейс) дает возможность иметь несколько лексиконов и переключатся между ними как угодно. К тому же есть возможность самому заниматься парсингом исходного кода, и можно отказатся от основного синтаксиса в виде слов разделяемых пробелами и перейти к любому другому.
                                                                                                            +7

                                                                                                            Читаю комментарии, споры об определениях… В рамках приведённых автором определений автор, как мне кажется, прав. Оппоненты оспаривают именно определения. Тут, похоже, конфликт религиозных убеждений — кто-то предпочитает академические определения, кто-то — прагматические.


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


                                                                                                            • А товарищ профессор говорит, что си — высокоуровневый язык!
                                                                                                            • ну да, ну да… Но высота у него такая низкая-низкая...

                                                                                                            Или даже так:


                                                                                                            • Язык си живёт на третьем этаже. Берусь утверждать, что его можно отнести к жителям нижних этажей. Дадим определение нижнего этажа: в современном мире, где строят от семнадцати этажей и выше...
                                                                                                            • коллега, Вы чудовищно неправы! Магазин можно открыть не выше первого этажа, всё что выше — уже слишком высоко!
                                                                                                            • Ну не всем же открывать магазины. Моей бабушке важна доступность без лифта. Низко!

                                                                                                            И так и спорят, каждый при своём.

                                                                                                              0
                                                                                                              если б запилили голосовалку, я б сказал что низкоуровневый!
                                                                                                                +1

                                                                                                                Резюмируя, можно подытожить, что язык С самый низкоуровневый из высокоуровневых языков))

                                                                                                                  0

                                                                                                                  Язык Си безусловно низкоуровневый, как и всякий другой, который может создавать бинарь, способную работать на голом железе (ОС сюда входит, вместе со всеми своими компонентами в виде драйверов, модулей… ).
                                                                                                                  По поводу конфликта интересов прикладных и системных разработчиков — здесь сложно однозначно говорить, что Си тому виной. К примеру, разработчики БД стараются сами управлять взаимодействием СУБД и дискового хранилища, чтобы получить максимум скорости, но, на мой взгляд, здесь больше борьба с ОС, чем с компилятором.
                                                                                                                  По поводу памяти, кто мешает запросить у ОС кусок памяти достаточно большого размера и самому рулить его нарезкой под свои нужды? А вот следить, чтобы он не ушел в swap, больше чем нужно — это уже опять борьба с ОС, и компилятор здесь ни при чем.
                                                                                                                  Аппаратные платформы совершенствуются. Все идет в сторону параллельного и асинхронного исполнения кода. Но винить Си в отставании, на мой взгляд странно. Потоки и процессы — это не аппаратная, а программная абстракция, и так, или иначе, но именно из этого языка их реализация ушла в другие, а не наоборот.
                                                                                                                  По поводу, например, расширенных инструкций процессоров от Intel терзают смутные сомнения, что компании AMD будет позволено их реализовать в полном объеме, а значит авторам компиляторов каждый раз придется решать: работаем везде, или по максимуму, но только здесь.
                                                                                                                  По поводу стандарта, это каждый решает для себя сам. Я только недавно выучил этот язык, и просто стараюсь не использовать конструкции, работу которых не понимаю. Про сам стандарт наслышан. Как по мне, 900 страниц — это капец как много. Стоит написать его с нуля, не привязываясь к существующим реализациям и не оставляя неопределенностей. Несоответствие стандарту автоматически не делает компилятор непригодным к использованию, но может мотивировать к исправлению проблем в них. Особенно, если о них постоянно будет кто — то напоминать.

                                                                                                                    +2
                                                                                                                    Язык, который может создавать бинарь классифицируется как компилируемый, уровень здесь совершенно ни при чем.
                                                                                                                      0
                                                                                                                      способную работать на голом железе

                                                                                                                      Т.е. без необходимости наличия операционной системы и/или виртуальной машины. Так-то и Java компилируемая, только компилируется в байткод, а не в самостоятельный бинарник.

                                                                                                                        0
                                                                                                                        а не в самостоятельный бинарник

                                                                                                                        можно и в бинарник, более того существуют полностью написанные на OC: JavaOS, jEmbryoS и JNode (в последнем, есть, правда микроядро с асемблером).
                                                                                                                        0

                                                                                                                        Здесь ключевой момент — это способность работать без ОС. Интерпретируемые языки, даже с промежуточным представлением кода, сразу мимо. Для компилируемых — все зависит от того, можно слинковать с бинарником все зависимости или нет (сюда входит и рантайм самого языка, который, теоретически, может зависеть от наличия тех или иных библиотек и сами библиотеки, которые используются программой).

                                                                                                                          0
                                                                                                                          Это что, 100500-я попытка подогнать понятие низкоуровнего языка под Си? )))

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

                                                                                                                          Промежуточное представление кода, и то, как исполняется программа на каком-то языке — это не часть языка, а деталь реализации. Для любого интерпретируемого языка можно написать компилятор, для любого компилируемого — интерпретатор. Просто для каких-то языков проще сделать первое, для других — второе.

                                                                                                                          Кстати, например система команд x86 тоже является промежуточным представлением, «ниже» идут микрокоды.
                                                                                                                            0
                                                                                                                            Для любого интерпретируемого языка можно написать компилятор

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

                                                                                                                              0
                                                                                                                              можно ли написать компилятор, например, JavaScript,

                                                                                                                              А разве в nodeJS не использует именно компилятор V8, переводящий скрипты непосредственно в машинный код? Вроде есть проекты, которые позволяют сделать и бинарник в виде exe файла.
                                                                                                                                0
                                                                                                                                Там же вроде JIT, код компилируется прямо во время работы, и из-за оптимизаций может быть разным при различных запусках в разных условиях с разными данными.

                                                                                                                                Вроде есть проекты, которые позволяют сделать и бинарник в виде exe файла.

                                                                                                                                А какие конкретно? Скорее всего там тупая упаковка js-движка и исходного кода на js в один файл.
                                                                                                                                0
                                                                                                                                можно ли написать компилятор, например, JavaScript, не запихнув в итоговый бинарник целиком движок JavaScript?

                                                                                                                                Запихнуть движок (в данном случае компилятор, раз мы хотим его компилировать) просто придется при использовании eval или Function, они его просто требуют, по другому никак.

                                                                                                                                И? Компиляция уже куда-то пропала, или что?
                                                                                                                                0

                                                                                                                                Си, как язык, абсолютно не требует подгонки подо что бы то ни было. Тем более, что и самого определения "системное программирование" в строгой и исчерпывающей форме вряд ли существует. Граница довольно сильно размыта. Например: ядро ОС безусловно системный компонент, командная оболочка csh/bash… еще, скорее всего, системная программа, утилиты, вроде find/wc уже ближе к прикладным, куда отнести KDE или XFce с гномом — это вообще тема холиварная. :)
                                                                                                                                Моя мысль была проще: если компилер может собрать самодостаточный файл, который можно запустить на голой железке, то язык, им реализуемый, можно назвать системным. И кстати, я не ограничивался одним Си. :))

                                                                                                                            0
                                                                                                                            Тут в последнее время шарписты всячески извращаются, один добавил в язык асмовставки, другой генерирует ассемблерный код для AVR. Относить ли после этого C# к низкоуровневым?
                                                                                                                              0

                                                                                                                              Ага, даже SIMD в C# уже завозят.
                                                                                                                              Да и вообще, является ли MSIL языком низкого уровня или нет? Ведь это же, по факту, ассемблер.

                                                                                                                              0
                                                                                                                              Язык Си безусловно низкоуровневый, как и всякий другой, который может создавать бинарь, способную работать на голом железе

                                                                                                                              Это свойство не языка, а компилятора. Никто не мешает написать компилятор, собирающий такой бинарь даже из js.
                                                                                                                              +3
                                                                                                                              Никто не станет спорить с тем, что язык ассемблера находится на самом низком уровне.
                                                                                                                              У нас так не бывает, чтобы никто не спорил. Verilog и VHDL двумя уровнями ниже ассемблера.
                                                                                                                                0

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


                                                                                                                                Но вообще да, есть и такой Verilog/VHDL, который, можно сказать, "компилируется в кремний". Условно: схемотехнический уровень < микрокод < язык ассемблера < C <… < декларативные DSL (или что там еще на вершину можно поставить)

                                                                                                                                +1
                                                                                                                                И всё же C — низкоуровневый язык

                                                                                                                                Да как вы его не называйте, каждый найдем в нем то, что ему нужно.
                                                                                                                                Одни на нем пишут операционные системы, другие, используя препроцессор, создают высокоуровневую среду разработки и т.д.
                                                                                                                                Си это Си и этим все сказано.

                                                                                                                                  0

                                                                                                                                  Поправка по поводу GPU: большинство современных видеокарт (NVIDIA, AMD, ARM Mali) используют модель SIMT (Single Instruction, Multiple Threads), а не SIMD. Что не отменяет того факта, что алгоритмы для них нужны другие, чем для CPU общего назначения, и, соответственно, набор эффективно решаемых задач на CPU и GPU не совпадает.

                                                                                                                                    0

                                                                                                                                    Если я все правильно понимаю, то SIMT — развитие подхода SIMD, не так ли? Т.е. GPU все же пытаются базовый подход (одна программа — разные данных) по возможности расширить.

                                                                                                                                      0

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


                                                                                                                                      А в NVIDIA Volta планировщик уже даже может при необходимости разделять потоки и вести для каждого отдельный стек и счётчик инструкций. Оптимизацией под Volta я пока не занимался, так что не могу сказать, насколько сильно это в реальности меняет подход к программированию.

                                                                                                                                        0

                                                                                                                                        Я, признаюсь, лучше знаю как работают векторные юниты в ЦПУ, принципы GPU знаю довольно поверхностно, без практической работы, так что могу только кивнуть :-)

                                                                                                                                    +1

                                                                                                                                    В статье не упомянуто главное — в Си есть UB, и компилятор совершенно спокойно к этому относится.


                                                                                                                                    В следующей версии gcc будет встраивать в программу с UB код для запуска ядерных ракет каждый раз, когда алиасят указатели.

                                                                                                                                    –1
                                                                                                                                    Никому ненужная попытка переписать терминологию.

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

                                                                                                                                    Предлагаю порассуждать об ОЗУ (устройство запоминающее оперативно, то есть быстро) и ПЗУ (устройство, которое постоянно что-то запоминает). Смысл будет примерно тот же.

                                                                                                                                    Кстати, а байт-код это высокоуровневый или низкоуровневый язык?
                                                                                                                                      0

                                                                                                                                      Люди пишущие на абстрактных языках для абстрактных виртуальных машин выражаются абстрактным языком об абстрактных сущностях… =)))
                                                                                                                                      А если по теме то, Си низкоуровневый или другими словами приближенный к машинному язык в том, что для каждой данной платформы данным компилятором сформируется условно одинаковый машинный код на условную операцию f(x) не зависимо от типа операнда, зависимость от разрядности единственное отличие в условностях.

                                                                                                                                        +1
                                                                                                                                        Спор о том, является ли C низкоуровневым языком кажется мне пустым. В моей скромной личной практике не так редко встречались задачи, для которых хотелось, чтобы в C имелись более «высокоуровневые» средства абстракции. А вот ситуации, когда не хватало низкоуровневых средств — можно пересчитать по пальцам, и это все были сильно платформозависимые моменты: запись в порт IO на PC, генерация прерывания, доступ к регистру флагов и т.д.
                                                                                                                                        Так что я бы сказал, что C достаточно низкоуровневый язык, по крайней мере для меня лично.
                                                                                                                                          –1
                                                                                                                                          Есть некоторая разница между низким уровнем и не достаточно высоким.
                                                                                                                                          0
                                                                                                                                          И все таки Си язык высокого уровня с возможностью низкоуровневого доступа, так как в ассемблере уровень абстракций данных это регистры процессора и ячейки памяти именованные через метки или EQU, именованные ячейки обычно глобальные (чтоб ограничить время жизни в локальных именованных ячейках придется писать пролог и эпилог в каждой процедуре).
                                                                                                                                            0
                                                                                                                                            А ведь и JAVA мог стать низкоуровневым языком…

                                                                                                                                            цитата (1996 год):

                                                                                                                                            Sun Microelectronics недавно выпустила первый Java-процессор — PicoJava I
                                                                                                                                              0

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

                                                                                                                                              0

                                                                                                                                              Тем временем сотрудники Intel заявляют "Rust — будущее системного программирования" :)

                                                                                                                                                0

                                                                                                                                                Очень может быть. :-) Прет как ледокол в последние годы.


                                                                                                                                                И вообще, в рамках аспектов, обсуждаемых в статье, Rust — продолжение С.

                                                                                                                                                0
                                                                                                                                                Прекрасно понимаю, почему автор называет С низкоуровневым, прекрасно понимаю тех, кто называет его не низкоуровневым.
                                                                                                                                                Найти правого невозможно без предварительных дефиниций.

                                                                                                                                                Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                                                                                                                Самое читаемое