- C — это не то, «как работает компьютер».
- Не думаю, что большинство людей говорят буквально, так что это неважно.
- Понимание контекста означает, что учить С по этой причине всё еще может иметь смысл, в зависимости от ваших целей.
Я планирую написать ещё две статьи с более подробным объяснением выводов, но этого уже достаточно. Добавлю сюда ссылки, когда статьи выйдут.
Я часто слышал от людей такое:
Изучая C, вы можете понять, как работают компьютеры.
Не думаю, что идея изначально неправильна, но она имеет некоторые оговорки. Если держать их в уме, то она вполне может быть жизнеспособной стратегией для изучения новых и важных вещей. Однако я редко вижу, чтобы люди подробно обсуждали данные оговорки, поэтому пишу эту статью, чтобы предоставить, по-моему, очень нужный контекст… Если вы думаете об изучении C для понимания работы компьютера, то статья для вас. Надеюсь, она поможет во всём разобраться.
Прежде чем мы действительно начнём, хотел бы сказать ещё кое-что: если хотите изучить C, то изучайте! Учиться — это здорово. Изучение C стало очень важным для моего понимания вычислительной техники и моей карьеры. Изучение этого языка и его места в истории языка программирования сделает вас лучшим программистом. Вам не нужно никакое оправдание. Изучайте вещи просто ради обучения. Эта статья призвана стать ориентиром, чтобы разобраться в истине, она не обсуждает, нужно или нет изучать С.
Прежде всего, кому вообще рекомендуется эта идея. Если вы пытаетесь «узнать, как работают компьютеры», то само собой разумеется, что вы в настоящее время этого не понимаете. Какие программисты не понимают, как работают компьютеры? Я в основном видел, что это чувство исходит от людей, которые в основном программируют на динамически типизированных «скриптовых» языках, таких как Ruby, Python или JavaScript. Они якобы «не знают, как работают компьютеры», потому что эти языки работают внутри виртуальной машины, где имеет значение только семантика виртуальной машины. В конце концов, вся идея виртуальной машины заключается в обеспечении переносимости. Цель в том, чтобы не зависеть от оборудования, на котором работает VM.
Есть только одна проблема: C тоже работает внутри виртуальной машины.
Абстрактная машина C
Из спецификации C99, раздел 5.1.2.3, «Выполнение программы»:
Семантические описания в этом Международном Стандарте описывают поведение абстрактной машины, в которой вопросы оптимизации не имеют значения.
На мой взгляд, это важнее всего понять при изучении C. Язык не «описывает, как работает компьютер», а описывает, как работает «абстрактная машина C». Всё остальное важное вытекает из этой концепции.
Еще одно замечание: здесь я выбрал C99, который не является последним стандартом C. Почему? Ну, в MSVC есть… интересная поддержка языка С, и в наши дни я пользователь Windows. Да, вы можете запускатьclang
иgcc
под Windows. Между C89, C99 и C11 не такая большая разница в отношении того, о чём мы говорим. В какой-то момент приходится выбирать. Версия, которую я здесь упомянул, включает в себя некоторые правки к первоначальной спецификации.
Возможно, в разговорах о C вы слышали ещё одну фразу: «C — переносимый ассемблер». Если задуматься об этой фразе, то вы поймёте, что если это правда, то C не может соответствовать работе компьютера: существует много разных компьютеров с разной архитектурой. Если C похож на ассемблер, который работает на разных компьютерах с разными архитектурами, то он не может одновременно функционировать в точности так, как каждый из этих компьютеров. Он должен прятать детали, иначе не будет переносимым!
Тем не менее, я думаю, что данный факт не имеет значения, потому что вряд ли люди буквально имеют в виду «C — это то, как работает компьютер». Прежде чем вернуться к этому, поговорим об абстрактной машине C, и почему многие, кажется, не понимают этот аспект языка C.
Отступление: почему люди заблуждаются?
Могу рассказать только о своём опыте, хотя наверняка он не уникален.
Я изучил GW-BASIC, потом С, потом С++, потом Java. Я слышал о Java до того, как начал писать на ней примерно в 1999 году, через четыре года после её появления. Маркетинг в то время активно противопоставлял Java и C++, он сосредоточился на JVM как платформе, и на том, что модель машины отличает её от C++, и, следовательно, C. Sun Microsystems больше не существует, но зеркало пресс-релиза напоминает нам:
Приложения на Java не зависят от платформы; нужно лишь портировать виртуальную машину Java на каждую платформу. Она действует как интерпретатор между компьютером пользователя и Java-приложением. Приложение, написанное в среде Java, может работать в любом месте, избавляя от необходимости переноса приложений на несколько платформ.
Главным девизом было «Пиши один раз, запускай везде». Эти два предложения стали тем, как я (и многие другие) пришёл к пониманию Java, и как она отличается от C++. У Java есть интерпретатор, виртуальная машина Java. В C++ нет виртуальной машины.
С таким мощным маркетингом «виртуальная машина» в умах многих людей стала синонимом «большой среды выполнения и/или интерпретатора». Языки без этой функции были слишком привязаны к конкретному компьютеру и требовали портирования, поскольку не являются по-настоящему независимыми от платформы. Главной причиной существования Java было изменение этого недостатка C++.
«Среда выполнения», «виртуальная машина» и «абстрактная машина» — разные слова для одного и того же фундаментального понятия. Но с тех пор они получили разные коннотации из-за незначительной дисперсии в реализациях этих идей.
Я лично считаю, что этот маркетинг 1995 года — причина, почему программисты до сих пор неправильно понимают природу C.
Так это утверждение ложно? Зачем Sun Microsystems тратить миллионы и миллионы долларов на пропаганду лжи? Если C тоже основан на абстрактной машине, которая предлагает переносимость между платформами, зачем нужна Java? Думаю, что это ключ к пониманию того, что люди действительно имеют ввиду, когда говорят «С — это то, как работает компьютер».
Что люди на самом деле имеют в виду?
Хотя C работает в контексте виртуальной машины, он по-прежнему значительно отличается от Java-подобных языков. Sun не врала. Чтобы понять, нужно знать историю С.
В 1969 году в Bell Labs была написана компьютерная операционная система на языке ассемблера. В 1970 году её окрестили UNIX. С течением времени Bell Labs покупала всё больше и больше новых компьютеров, включая PDP-11.
Когда пришло время портировать Unix на PDP-11, они решили использовать язык более высокого уровня, что было довольно радикальной идеей в то время. Представьте, что сегодня я вам скажу: «Я собираюсь написать ОС на Java» — вероятно, вы будете смеяться, хотя идея реализуема. Ситуация (в моём понимании, я тогда не жил) была примерно аналогичной. Рассматривался язык под названием B, но он не поддерживал некоторые функции, которые были у PDP-11, и поэтому они создали преемника, назвав его "C", поскольку это была следующая буква в алфавите.
Языка "A" не было; B стал преемником BCPL (Basic Combined Programming Language).
В 1972 году на PDP-11 написали первый компилятор C и одновременно переписали UNIX на C. Изначально о переносимости не думали, но C получил известность, так что компиляторы C портировали на другие системы.
В 1978 году вышло первое издание книги «Язык программирования С». Ласково именуемая «K&R», по именам её авторов, книга совсем не была похожа на спецификацию, но при этом достаточно подробно описывала язык, в результате чего другие тоже попытались написать компиляторы С. Позже эту «версию» будут называть «K&R C».
По мере распространения UNIX и C их обоих портировали на многие компьютеры. В 70-х и 80-х годах их аппаратная база непрерывно росла. Точно так же, как C создали, потому что B не поддерживал все функции PDP-11, многие компиляторы использовали расширения языка. Поскольку существовал только K&R, а не спецификация, то это считалось приемлемым, пока расширения были достаточно близки. К 1983 году отсутствие какой-либо стандартизации стало вызывать проблемы, поэтому в ANSI создали группу для подготовки спецификации. В 1989 году вышел стандарт C89, который иногда называется "ANSI C".
Спецификация C пыталась унифицировать эти разнообразные реализации на различном оборудовании. Таким образом, абстрактная машина C — это своего рода минимально возможная спецификация, которая позволила бы одному и тому же коду работать одинаково на всех платформах. Реализации C компилировались, а не интерпретировались, поэтому не было интерпретатора, поэтому не было "VM" в том смысле 1995 года. Однако программы на языке C пишутся на этом абстрактном несуществующем компьютере, а затем код преобразуется в ассемблер, специфичный для конкретного компьютера, на котором выполняется программа. Вы не могли полагаться на некоторые конкретные детали для написания переносимого кода на С. Это делает написание переносимого C очень сложным, так как вы, возможно, сделали специфичное для платформы предположение при написании начальной версии своего кода.
Это лучше всего иллюстрируется примером. Одним из основных типов данных в языке C является
char
, от слова «символ». Однако абстрактная машина C не определяет, сколько бит должно быть в char
. Ну, определяет, но не числом; она определяет размер CHAR_BIT
, который является константой. Раздел 5.2.4.2.1 спецификации:Приведённые ниже значения должны быть заменены константными выражениями, подходящими или используемыми в директивах предобработки#if
.… Значения в конкретных реализациях должны быть равны или больше по величине (абсолютное значение) тех, которые приведены здесь, с тем же знаком.
CHAR_BIT: 8
Другими словами, вы знаете, что
char
составляет не менее 8 бит, но реализации могут быть больше. Чтобы правильно кодировать «абстрактную машину C», в качестве размера при обработке char
необходимо использовать CHAR_BIT
вместо 8
. Но это не какая-то функция интерпретатора, как мы думаем о виртуальных машинах; это свойство того, как компилятор переводит исходный код в машинный код.Да, есть системы, гдеCHAR_BIT
не8
.
Таким образом, эта «абстрактная машина», хотя технически и является той же идеей, что и виртуальная машина Java, скорее представляет собой конструкцию компиляции для управления компиляторами при создании ассемблерного кода, а не какой-то проверкой в рантайме или свойством. Эквивалентный тип в Java — это
byte
, который всегда составляет 8 бит, а на реализацию JVM возлагается задача, что делать на платформах, где байт больше. (Не уверен, работает ли JVM на любой из этих платформ, но именно так это должно функционировать). Абстрактная машина C создана как минимальная обёртка для различного «железа», а не в качестве какой-то платформы из цельной ткани, написанной в софте для вашего кода.Таким образом, хотя Sun была технически не права, на практике они имеют в виду немного не то, что буквально говорят, и что они имеют в виду — верно. То же самое с фразой «Изучай C, чтобы понять, как работают компьютеры».
Изучай С, чтобы ЛУЧШЕ понять, как работают компьютеры
Что на самом деле люди имеют в виду? В контексте «должен ли рубист изучать C, чтобы понять, как работают компьютеры» — это совет снизиться «до уровня железа». То есть не только понять, как своя программа работает внутри виртуальной машины, но и как сочетание программы и VM работают в контексте самой машины.
Изучение C обеспечит вам больше таких деталей, потому что абстрактная машина гораздо ближе к аппаратному обеспечению, а также абстракциям операционных систем. Язык C сильно отличается от языков высокого уровня, поэтому его изучение может многому научить.
Но важно помнить, что C по сути является абстракцией аппаратного обеспечения, а абстракции несовершенны. Осторожнее с тем, что делает C или как он работает с самой машиной. Если слишком углубиться, то вы обязательно столкнетесь с этими различиями, что может вызвать проблемы. Большинство учебных ресурсов для C, особенно сегодня, когда оборудование становится все более гомогенным, будет продвигать идею о том, что именно так работает компьютер. Поэтому ученику может быть трудно понять, что происходит под капотом и что такое абстракция, предоставленная C.
В этой дискуссии мы даже не затронули другие вопросы. Например, что из-за огромной популярности C аппаратное обеспечение стало более однородным, потому что оно имеет тенденцию двигаться к семантике абстрактной машины C. Если ваша архитектура слишком сильно отличается от семантики языка C, программы на языке C могут работать намного медленнее, чем другие, а скорость аппаратного обеспечения часто измеряется тестами на языке C. Эта статья и так достаточно длинная…
По этой причине я думаю, что более точная версия этого утверждения будет «Изучая C, вы больше узнаете о том, как работают компьютеры». Я действительно думаю, что примерное знакомство с C полезно многим программистам, даже если они сами не пишут C. Знакомство с C также даст вам представление об истории развития нашей отрасли.
Есть и другие способы изучить эту тему; C по своей сути не предназначен для изучения компьютера, но это хороший вариант.
В программировании так много всего, чему стоит поучиться. Желаю вам успехов на этом пути.
Комментарии (98)
3dcryx
19.10.2018 12:31"Если ваша архитектура слишком сильно отличается от семантики языка C, программы на языке C могут работать намного медленнее, чем другие, а скорость аппаратного обеспечения часто измеряется тестами на языке C."
У меня мозг кипит от этой фразы.
Это значит, что есть архитектуры для которых сложно написать эффиктивный компилятор С? При этом на эти же самые архитектуры кто-то пишет бенчмарки на С?Jef239
19.10.2018 18:11Это значит, что есть архитектуры для которых сложно написать эффиктивный компилятор С?
Да, ПЛК, например. Когда машина аппаратно заточена под ladder. Кстати, ассемблер ПЛК более-менее стандартизован.
Там одна из проблем — очень неудобно циклы делать. Ну совсем для другого стиля предназначен процессор.Vanellope
20.10.2018 05:34Совсем не факт, что процессоры ПЛК имеют свою особенную архитектуру. Все эти ладдеры и ассемблеры — программа прикладного уровня, возможно, исполняемая в интерпретаторе в ОС ПЛК, либо преобразуемая в некий байткод. А придуманы они, чтобы прикладной программист не наступал на грабли. Внутри же ПЛК те же АРМы, интелы и даже 8-битные AVR, и крутятся там РТОСы и прочие ОСы, от Линукса до ДОСа. И написаны они наверняка на С.
Jef239
20.10.2018 21:59-1В этом смысле и X86 — всего лишь байт-код для интерпретации микропрограммами. А внутри любого современного X86 сидит RISC-процессор с совсем иной архитектурой.
Да и вообще, с момента появления микропрограмм, реальная архитектура процессора начала сильно расходится с внешней системой команд. Тут вспоминается Наири-4 и Наири-3, которые на микропрограммном уровне эмулировали чужую систему команд.
Что касается ПЛК — то были и физические процессоры с такой архитектурой, были и эмуляции на базе иных профессоров, была и смешанная модель, когда процессоров было 3 — однобитный для логики, словный для словных команд и связной для работы с сетью.
А придуманы они, чтобы прикладной программист не наступал на грабли
Вот за это вам и минус.
Дело не в написании кода, дело в эксплуатации. Большая система, 8 тысяч датчиков и 2 тысячи выходов. Как думаете, какой шанс, что все будет работать? Да нулевой. И при поломке счет идет на минуты. Больше 3х часов в месяц потеряно — премия снимается со всей службы. И вот тут, при эксплуатации, ladder выигрывает в десятки раз у Си-подобных языков. Все сигналы видны в графическом виде. И время обхода поломки занимает порядка 2-3 минут.
То есть за 2-3 минуты меняется код программы так, чтобы обойти сбоящий датчик. Попробуйте повторить это на Си.Vanellope
21.10.2018 06:37Спасибо за экскурс в историю, но в настоящее время ограничивать возможности ПЛК путем применения различных процессоров никто не будет. А если ПИД регулятор надо добавить, значит DSP дополнительно добавлять будем? Хардварный Ladder это тоже неплохо, можно и на ПЛИС реализовать, но здесь опять же принудительное ограничение функционала, сейчас даже простые программируемые реле умеют не только логику, но и аналоговый ввод/вывод, регуляторы и коммуникационные возможности, причем функционал расширяется сменой прошивки. А выполнять много-много тасков, с различной периодичностью вызова?
Большое преимущество современных ПЛК — в возможности выбора, какой язык использовать для проекта или даже для отдельного POU. Это позволяет взять LD для простых логических выражений (замена релейных схем же), но для ПИД использовать уже FBD, а для сложной математики или обработки строк — ST. И выстрелить в ногу не получится — в чужую область памяти через указатели не попадешь, стек не переполнишь, неопределенного поведения не будет.
За минус спасибо, но дело не только в эксплуатации. Программирование это что? Алгоритм, программная реализация и отладка. LD и FBD (а тем более SFC) позволяют легко представить алгоритм в виде программы, что позволяет избежать возможных ошибок на этапе программной реализации, а затем упростить процесс отладки. Поиск отказавшего датчика — тот же процесс отладки, но вот попробуйте разобраться в проблемах сетевого обмена на LD или IL. А еще если надо поддерживать код, написанный неизвестно когда и неизвестно кем. Вот сейчас на работе есть задача разобраться с кодом для старенького TSX, программа вся на ST и абсолютно без символьных таблиц, сплошной
"IF((%MW9241=%MW11061 AND %MW9249=%MW11069)OR
(%MW9257=%MW11077 AND %MW9265=%MW11085)OR
(%MW9273=%MW11093 AND %MW9281=%MW11101))AND
(%MW18313=%MW9213)AND(%MW18314=%MW9214)AND
(%MW18315=%MW9215)THEN
SET %M221;
ELSE
RESET %M221;
END_IF;"
Вот где настоящая археология по листингам дизассемблера.Jef239
21.10.2018 09:21Такое впечатление, что вы продаете ПЛК Simatic. От ПЛК прежде всего нужна наработка на отказ порядка 20 лет, а не скорость. Скана в 20-30 мс обычно хватает. Много тасков — очень прилично снижает надежность.
попробуйте разобраться в проблемах сетевого обмена на LD или IL.
Я как раз этот кусок и писал. Написал, впятером проверили, код подписали и 15 лет работает.
в чужую область памяти через указатели не попадешь,
Почему? На IL/LD/FBD в OMRON — вполне читается и пишется. Более того, можно программно и блокировки ставить. Прямо из IL/LD/FBD.
программа вся на ST и абсолютно без символьных таблиц, сплошной
А это ответ на вопрос, почему ladder лучше ST или Си. Лучше в LD диасссемблируйте, код понятней будет.
«IF((%MW9241=%MW11061 AND %MW9249=%MW11069)OR
Просто у меня опыт на OMRON, а у вас на Simatic. Вот и смотрит каждый со своей колокольни.
shiru8bit
19.10.2018 12:38-2Я часто слышал от людей такое
Никогда не слышал от людей такого. Вот совсем. И учитывая, что плюсы не упоминаются, а компиляторов чистого C и книг по нему уже очень давно так просто не найти, это какая-то окаменелость особого периода, когда такая постановка вопроса могла быть актуальна.alexxisr
19.10.2018 13:38gcc умеет чистый с.
K&R + стандарт покрывают 99% знаний о С.
Но для изучения «как работает компьютер» по-моему лучше ассемблер.shiru8bit
19.10.2018 13:46Когда я учил C, в районе конца прошлого века, книжек именно по C так и не нашлось, а все доступные компиляторы и литература были для плюсов (BC++3.1, BC++5, BC++B3, MSVC6). Да, конечно, обратная совместимость есть, но при вхождении с нулевым уровнем сходу разобраться, где плюсы, а где нет, и в чём разница, просто невозможно. То есть, если сейчас человек захочет с нуля изучить чистый C, да ещё сидя под Windows или Linux, у него будут определённые затруднения. А плюсы, особенно современные, 'для понимания работы компьютера' уже примерно так же далеки от железа, как любые другие современные же ЯВУ.
Однозначно, для поставленной задачи лучше подходит ассемблер.alexxisr
19.10.2018 14:25gcc -std=c99
(стандарт можно выбрать по вкусу) — компилирует чистый С, ругаясь на всё с++ -ное
так что проблем с компилятором в наше время нет, даже под виндой
Я не уверен, но кажется был и досовский ТурбоС, который не С++.
Керниган с сотоварищем написали свою книгу тоже не в этом тысячелетии — я ее читал как раз в конце 90х.
Так что было бы желание.shiru8bit
19.10.2018 20:57Человеку искушённому всё кажется простым и понятным, 'было бы желание'. Но новички ещё не обладают знанием, что им надо искать, как им надо искать, и зачем. Новичок (человек, не знающий, как работает компьютер) с gcc, разбирающийся в стандартах и ключах командной строки — это очень вряд ли. Да что там, знание командной строки для значительного количества современных программистов — это просто чёрная магия какая-то. Самый популярный баг-репорт, который я регулярно получаю — 'ваша программа не работает, показывает чёрное окно и закрывается!'.
dimaviolinist
19.10.2018 18:24
Ogra
19.10.2018 12:46Ручное управление памятью, ассемблерные вставки и/или интринсики, просмотр скомпилированного ассемблерного когда в отладчике.
Можно увидеть как компилятор кладет переменную в регистр, и можно предотвратить это с помощью volatile. Можно сравить Array-of-structures и structure-of-arrays, и влияние процессорного кэша на производительность.
На вопрос «нужно ли учить С для понимания работы компьютера» — ответ однозначен, «да, нужно». Только Си и его инструменты позволяют это сделать. Понятно, что можно просто выучить Си и не понимать, как работает компьютер, но вопрос то сформулирован по-другому. Если вы хотите понять, как работает компьютер — учите Си.cheshircat
19.10.2018 13:24+2Я считаю если цель стоит понять как работает компьютер, то нужно изучить язык Ассемблера.
Во-первых, это не так сложно как многие думают. Команд довольно не много и они выполняют простые действия. Нет никаких сложных конструкций, все на уровне запиши/считай из ячейки/регистра.
Во-вторых, видишь почти все тоже самое что и процессор. В отладчике все те же самые инструкции и можно посмотреть как они влияют на регистры, память — это не абстрактные переменные, а вполне реальные ячейки по 1 байту. Соприкасаешься с прерываниями, двоичной арифметикой и прочим. И начинаешь понимать те вещи которые от тебя скрывают все более высокоуровневые языки, даже C.
Поэтому если хочется действительно понять как работает компьютер, нужно сесть и изучить ассемблер.
А после этого можно уже садиться за C, ну если он нужен.
Что касается фразы: «хочешь понять как работает компьютер, изучай C». Я думаю, это уже сродни мифу, который поддерживают люди не писавшие на C/C++. Потому что на С пишут ОС, драйвера и многое другое «низоуровневое» и он представляется как что-то сложное, работающее чуть ли не на уровне CPU. :)Ogra
19.10.2018 13:47+1Потому что на С пишут ОС, драйвера и многое другое
Ассемблер необходим для понимания, как работает процессор. Если к этому добавить ОС и драйвера (а это уже уровень Си), то и получится понимание работы компьютера.
Си — нужен, но не достаточен, асм тоже надо.
LynXzp
19.10.2018 13:56Для тех кому тоже интересно: тут считают количество инструкций (1-3.5k), тут хороший список.
P.S. думаю что ассемблера не достаточно… чтобы понять как работает процессор (потому что кеш, конвейер, предвыборка, ME,...). Интересно и просто можно маленькую часть из этих вещей узнать в книге Криса Касперски «Техника оптимизации программ. Эффективное использование памяти». (просто вспомнилось, хорошее чтиво, но явно не учебник о том «как работает компьютер»)cheshircat
19.10.2018 14:13Ассемблера 80186 вполне достаточно для образовательных целей. А там Вряд ли в наше время стоит писать программы на ассемблере для PC.
… чтобы понять как работает процессор
Дальше можно поизучать как перемещаются электроны и т.п. В теме стоит вопрос изучить как работает компьютер, я думаю стоит остановиться на этом уровне абстракции :) По этому поводу мне очень нравится ответ Ричарда Феймана про магнитыLynXzp
19.10.2018 15:17Ну и правильно Фейнман говорит — смотря кому отвечать (или зачем ему нужны эти знания), если нужно написать драйвер для USB сигнализации то даже C не обязательно, лишь бы было совместимо с требуемой ОС. Если оптимизируется кодек то ассемблера уже маловато. А если познание ради познания то и устройство транзисторов с квантовой физикой будут тоже подходящими. Первые два примера — классический формат хабра, последний — гиктаймса. Нельзя просто так взять и остановиться. Всегда кому-то захочется to go deeper. В принципе получается что «нужно учить С для понимания работы компьютера» в первом примере что я привел. В других примерах это выражение будет ложным.
BiosUefi
19.10.2018 14:06А что тогда ближе? ООП, лямбда-исчисления, или функциональное программирование?
sami777
19.10.2018 14:241. Нет никакого смысла собирать код на Си в ассемблер. Сразу создается двоичный код. В своем первом компиляторе для этого было меню «Disassembly», что предполагало возможность создания ассемблерного листинга из двоичного кода.
2. Byte в яве строго типизирован и имеет размер тип unssigned. Просто char предполагает signed, т. е. сравнение в явном виде недопустимо.
3. Если вы считаете, что изучение Си позволит вам понять как работает компьютер, то я скажу, что изучение ассемблера 86 позволит вам стать богом в копьютерном понимание. Что значит, что Си всегда есть и будет языком высокого уровня и никакого понимания, как работает компьютер он вам не даст!.. Ну разве что вы не работает на каком нибудь питоне или другом «безобразно высоком» языке программирования.Ogra
19.10.2018 14:35Нет никакого смысла собирать код на Си в ассемблер.
Про clang и llvm слышали?sami777
19.10.2018 21:30Уверен на 100%, что шланг при компоновке двоичного кода из Си исходника не нуждается в компиляции Си кода в ассемблер! И что создание ассемблерного листинга это не более, чем плюшка и по сути к созданию бинарного кода никакого отношения не имеет!
Но если вы считаете иначе, то мне будет очень интересно почитать из какого нибудь серьезного источника, что шланг обязательно пере собирает код под ассемблер, чтобы в последствии скомпоновать исполнительный файл.
0xd34df00d
19.10.2018 16:35Просто char предполагает signed
Нет, просто char ничего не предполагает, поэтому технически в C три типа: char, signed char, unsigned char.
axe_chita
21.10.2018 06:53Таки не правы насчет char
Тип Длина в битах Диапазон
char 8 от-128 до 127
unsigned char 8 от 0 до 255
signed char 8 от-128 до 127
int 16 от-32768 до 32767
unsigned int 16 от 0 до 65535
signed int 16 от -32768 до 32767
short int 16 от -32768 до 32767
unsigned short int 16 от 0 до 65535
signed short int 16 от -32768 до 32767
long int 32 от -2147483648 до 2147483647
unsigned long int 32 от 0 до 4294967295
signed long int. 32 от -2147483648 до 2147483647
Использование signed для целочисленных типов является избыточным (но допустимым), поскольку объявление целочисленных типов по умолчанию предполагает знаковое число.0xd34df00d
21.10.2018 07:53Ваш источник некорректен (а кстати, какой он)? Я вот взял драфт стандарта С тута.
char 8 от-128 до 127
Процитирую 5.2.4.2.1/2:
If the value of an object of type char is treated as a signed integer when used in an
expression, the value of CHAR_MIN shall be the same as that of SCHAR_MIN and the
value of CHAR_MAX shall be the same as that of SCHAR_MAX. Otherwise, the value of
CHAR_MIN shall be 0 and the value of CHAR_MAX shall be the same as that of
UCHAR_MAX.Более того, из /1 следует, что остальная часть вашей цитаты тоже, вообще говоря, не совсем корректна.
Далее, 6.2.5/4:
There are five standard signed integer types, designated as signed char, short
int, int, long int, and long long int.Обратите внимание, что у signed char единственного в списке явно указан signed.
6.2.5/14 вместе с /15 окончательно закрывают вопрос:
The type char, the signed and unsigned integer types, and the floating types are
collectively called the basic types. Even if the implementation defines two or more basic
types to have the same representation, they are nevertheless different types.и соответственно
The three types char, signed char, and unsigned char are collectively called
the character types. The implementation shall define char to have the same range35,
representation, and behavior as either signed char or unsigned char.Сноска 35 закрывает вопрос ну совсем окончательно:
CHAR_MIN, defined in <limits.h>, will have one of the values 0 or SCHAR_MIN, and this can be
used to distinguish the two options. Irrespective of the choice made, char is a separate type from the
other two and is not compatible with either.Такие дела.
finlandcoder
19.10.2018 14:30Си учить не нужно. Можно просто на нём покодить на предмете «Архитектура ЭВМ». Посмотреть на сколько хватит рекурсии. Сколько можно выделить памяти и упасть. Какой максимальный фаил можно прочитать. Как работает стек и хип. Когда изучают разницу между x32 и x64.
semibiotic
19.10.2018 14:45Если человек, действительно, хочет понимать как работает компьютер, ему нужно познакомиться с языком ассемблера (хотя бы одним).
Си, с учетом UB-догм, а тем более с оптимизацией, переходящей черты здравого смысла, в разы сложнее.
maxzhurkin
20.10.2018 22:09Существование UB — всего лишь отражение того факта, сто синтаксическая сторона C мощнее семантической — было бы странно и неудобно, если бы было наоборот, или если бы были и места в семантике, которые невозможно выразить в тексте, и текст, который не имеет конкретного семантического выражения (собственно, суть UB)
martin_wanderer
19.10.2018 15:06Сам вопрос какой-то странный: чтобы понимать работу компьютера надо надо таки понимать, как он устроен. Это, например, рассказывают в вузовском курсе «Архитектура микропроцессорных систем.
А Си, конечно, довольно близок к железу, но все же придуман специально для того, чтобы от этого железа абстрагироваться хоть на сколько-то. Впрочем, об этом и статья.
Wilk
19.10.2018 16:07Здравствуйте!
Я чаще слышу высказывание «чтобы понять, как работает процессор/компьютер/микроконтроллер нужно изучить ассемблер». И я не вполне согласен с данным высказыванием.
Я не специалист в области, но когда читаю про конвейеры, предикторы, кэши и векторизацию, я слабо представляю, как понимание всех этих механизмов связано с ассемблером. При этом мне кажется, что понимание именно данных механизмов позволяет понимать, как работает процессор. Для понимания того, как работает компьютер нужно ещё что-то знать про то, как работают различные периферийные устройства процессора, что, опять же, выходит за рамки ассемблера.
В случае микроконтроллеров ситуация в моём понимании аналогична: для понимания того, как работает конкретное устройство, достаточно прочитать документацию и описание архитектуры. Да, на контроллерах часто приходится выполнять манипуляции над регистрами для управления периферийными устройствами, но делать это в результате можно и из C, и из C++, и из чего угодно, для чего есть компилятор. Намного более важным является понимание того, как получить желаемый результат.Gryphon88
19.10.2018 16:38Изучение низкоуровневых вещей идёт двумя путями:
— Целенаправленно стреляем себе в ногу
— Пытаемся «сделать хорошо», но на ногу всё равно смотрим.
Ассемблер гарантирует, что дырка в ноге появится сразу и в предсказуемом месте. В плюсах дырок может быть десяток и появятся они в течение месяца.
Выше уже давали список инструкций х86, теоретически, можно залезть почти куда угодно, кроме предсказателя ветвлений. Для последнего (а точнее, его обмана) пишут книги по оптимизации программ.
saipr
19.10.2018 16:26Вообще классическое и исчерпывающее описание языка C на четырех страницах было дано Эндрю Таненбаумом в приложении к учебному пособию «Operating Systems: Design and Implementation» (1987, ISBN 0-13-637406-9). Там же на паре страницах был описан и ассемблер. Это классика.
k1p1sh
19.10.2018 18:46Вот хотел оценки постам поставить, а оказывается не могу, но могу писать!
— Касаемо темы: Язык Си не позволяет понять принципы работы компьютера! Си позволяет понять работу Операционной системы и не более, в нем (как и указано в статье) есть очень тонкие моменты по части распределения памяти и при этом отсутствуют любые регуляторы, которые в более высоких языках (читать компиляторах и интерпритаторах) присутствуют, для контроля утечек и переполнений.
— Из выше сказанного: Обучать чайника языку СИ ну просто не логично! это язык профессионалов, уже знакомых с понятием «утечка» и «переполнение»! Для чайников есть множество сред, в которых они точно также поймут всю суть «работы компьютера» — Камень в огород Сишников — А вы знаете как работает компьютер? (Вот я знаю! знаю не один десяток процов.)axe_chita
19.10.2018 19:29Вы Forth-еров спросите, они вам в польской нотации, с использованием стека, и правила «если определение не влазит на один экран, то его надо резать на более мелкие части», они вам объяснят как работает компьютер.
Йоды магистра речи тайна раскрыта, оказывается, на форте программист старый есть он просто.
Sdima1357
19.10.2018 19:37«С» работает и при отсутствии операционной системы.
«интерпритатор» — Интерпретатор
С в общем то, все таки язык достаточно высокого уровня для представления сложных абстракций и программ с сохранением читаемости кода, чего нельзя сказать об ассемблере.
Архитектура процессоров последних поколений, в значительной мере заточена к эффективному выполнению «с» -шных программ и развивалась и под его влиянием в том числе.
Знаю десяток
А я вот начинал с фортрана /алгола на БЭСМ 6 и пишу до сих пор, от встроенных до GPU, и не могу сказать что в совершенстве знаю архитектуры процессоров, все время приходится курить мануалы.
unxed
20.10.2018 01:08При наличии таких инструментов как rust, начинать знакомство с программированием и устройством компьютера с инструмента, созданного, чтобы стрелять себе в ногу, потому что ничего лучше на момент его создания изобрести не успели?
Ну, хм.
Реально, язык, для которого пришлось придумывать концепцию статических анализаторов, чтобы хоть как-то обойти изначальные дефекты архитектуры. Просто идеальная модель для обучения тому, как не надо делать (полагаться на внимательность людей, например).0xd34df00d
20.10.2018 07:00При наличии таких формализмов, как лямбда-исчисление и построенные поверх него системы типов, начинать знакомство с программированием…
maxzhurkin
20.10.2018 22:17А вы транспортом пользуетесь?
Это же ярчайший пример того, как не надо делать (езда на транспорте невозможна без того, чтобы полагаться на внимательность людей, как свою, так и других)!
Goron_Dekar
Очень нехватает в этой статье конкретезации того, о каком компьютере идёт речь.
Если о тех, которых большинство, то С самое близкое к тому, как они работают. Ну нет у большинства arm'ов и Pic'ов ни векторизации, ни предиктора.
LexB
Смотря что значит «понимать как работает компьютер», если совсем понимать, то начинать стоит с ассемблера.
Goron_Dekar
Ассемблер у всех компьютеров разный. С один.
shiru8bit
C тоже бывает разный. Разные стандарты, особенно до C89, разные ограничения, разные библиотеки. На одной платформе нет stdlib вообще, на другой нет динамического выделения памяти, на третьей нет float, разные размеры переменных, на древностях нет const, enum, и так далее.
Довольно интересные ощущения возникают, если изучить C на чём-то современнее, чем Turbo C, а потом увидеть старообрядный код, типа:
LynXzp
Или так:
axe_chita
Не покатит код, компилятор выдаст ошибку необъявленная переменная Z, плюс вывесит предупреждение необходимо приведение типов
LynXzp
Ну я же наверное знал что писал, не просто так выдумал? (Обратное тоже возможно, но как-то маловероятно на хабре) Есть такое правило «int по умолчанию» для старого С, функции по умолчанию возвращают int, даже если нет return (п.9), параметры по умолчанию int. Компилирую в gcc 6.4.0 без опций:
Предупреждения есть, и было бы странно если бы их не было, но код компилируется и выполняется. Clang 6.0 и 7.0 аналогично, даже не смотря на то что его вообще не было когда такой синтаксис использовался.axe_chita
Естественно функция должна возвращать значение, иначе какая она тогда функция? возвращаемый тип void и был добавлен стандарт ANSI C и во многих компиляторах вышедших незадолго до его принятия делали такую великолепную заглушку #define void int чтобы сделать текст программы более совместимым.
На на счет использования необъявленной переменной, и без ошибки при компиляции… Ну это какойто BASIC, причем до 90-х годов, если не до 80-х. Жуть и дичь.
LynXzp
> Какая же она функция без возвращения значения?
Нормальная функция, мы же не в рамках паскаля говорим с его процедурами.
> BASIC до 90-х
Или современный php. Но в моем коде нет необъявленной переменной, z это параметр функции, он объявлен. Просто так не объявлять переменную нельзя.
axe_chita
[Зануда моде он]
Функция_(программирование)
Подпрограмма
[Зануда моде офф]
В вашем коде z это параметр/переменная функции, и тип её НЕОПРЕДЕЛЕН, максимум что мы можем сказать про неё что ее класс хранения auto и это все. Возможно в ANSI C можно былобы написать определение функции int foo(int x,z), но для K&R данный выкрутас должен быть ошибкой. И это ошибка должна ловится компилятором, в ином случае авторов компилятора необходимо садить за стол, заставлять ложить руки на крышку стола, и с оттягом длинной деревянной линейкой по пальцам охаживать, приговаривая «что необъявленно, того не существует, нет никакого правила (по умолчанию), это ересь — не плоди её». Если вам такой метод кажется жестоким, могу предложить ремень как инструмент патча.
vvzvlad
Функция, которая что-нибудь делает. Начиная от работы с железом и заканчивая изменением данных по указателю.
0xd34df00d
У вас просто глобальный стейт ? неявно таскается с собой везде в таких языках, и функция с аргументом типа T? возвращаемым значением типа T? на самом деле является функцией из T??? в T???.
Либо это функция, которая никогда не возвращает управление (
abort
, например).Gryphon88
Глобальное состояние в тех же микроконтроллерах банально есть, хранить неизменяемую копию неудобно и бессмысленно, тут делать вид, что глобального состояния нет, себе дороже. Как можно передать состояние, если оно может быть изменено по внешнему или аппаратному событию событию? Проще явно или неявно держать его в голове.
0xd34df00d
Это вопрос другого уровня абстракции.
А это более интересный вопрос, но тоже легко решаемый с точки зрения семантики языка.
Gryphon88
Честно говоря, не видел ФП на микроконтроллерах. Ниже «пионерский» код отправки байта через аппаратный UART на AVR. Тут мы читаем 2 регистра состояния (глобальное состояние) и пишем в регистр без кода возврата (грязная функция).
Как тут можно уйти от глобального состояния и сайд-эффектов? Я не троллю, но правда не понимаю, как мешать IO с ФП.0xd34df00d
А не надо от него уходить, его надо учитывать (и сделать неглобальным).
ФП-код спокойно работает, например, с сетью и с любым другим внешним миром. Байты читать-писать с этой точки зрения никак не отличается. Будет у вас вместо этого какое-нибудь
Функция имеет сайд-эффекты, функция читает и пишет во внешний мир, функция имеет тип
IO smth
.Gryphon88
Насколько я понимаю, работа с сетью на компьютере должна отличаться от работа с вводом-выводом на МК. Сетевая карта, получив пакет, дёргает прерывание и в нём перекладывает пакет в собственный буфер, а чаще в память, откуда пользовательская программа данные и берёт. В МК предотвращение «застревания» данных (а иногда и сброс флагов прерываний) лежит на программисте. Т.е. мы имеем ситуацию, когда ресурс может модифицироваться как программно (из main loop или обработчика прерывания), так и чисто аппаратно (из внешнего мира или при вызове другого прерывания, если вложенные прерывания запрещены). Нет никакой гарантии, что в
IO smth
мы вообще попадём. Тут вопрос, как не тащить в обработчики приёма-передачи состояние всех регистров, которые могут влиять на состояние интересующего нас регистра.0xd34df00d
Это, опять же, на самом деле вопрос формализации семантики языка, ФП тут по большому счёту ни при чём.
Это всё можно расписать в терминах состояния вычислительной машины и small-step evaluation semantics. Например, перемежая каждый шаг вычисления вашей программы с шагом обновления состояния железа. Вы по-прежнему думаете о ваших функциях как явно работающих с внешним состоянием, просто кроме них ещё есть внешняя среда. Аппаратные прерывания там, не знаю.
Тут, кстати, нет вообще никакой разницы, аппаратное прерывание поменяло это самое глобальное состояние, или же настолько же внешний для вас драйвер сетевой карты пополам с ОС.
Gryphon88
Можно, но на глазок — очень дорого. И я не совсем понимаю, зачем, во многих случаях дешевле подольше посидеть с отладчиком, чем брать камень, который сможет одновременно тянуть основную задачу и вести самопроверку.
0xd34df00d
Эх.
Причём тут самопроверка? Я говорю только о формализации языка. Чтобы вы, ну, могли о нём рассуждать как-то чуть более формально, чем на глазок. После таковой формализации вам никто не мешает компилировать его в произвольно низкоуровневое представление (точно так же, как и хаскель, например, хаскель тот же в рантайме тоже самопроверкой не занимается, и про типы в рантайме он не знает вообще ничего, что, кстати, даже проблематично, если вы пишете завтипизированный код, но это совсем другая история).
Вон, за что в некоторых кругах джаву (или некоторое её подмножество) любят — про неё просто сели, взяли и доказали, что выполняются progress и preservation теоремы, а это означает, что в ней физически нет UB.
Gryphon88
Значит, я что-то не понял.
Как я это прочитал:— Заводим список регистров, в прерываниях обновляем состояние
— Вешаем на таймер планировщик через высокоприоритетное прерывание, в котором, допустим, на нечётных тиках прерывания глобально разрешены, а на чётных — запрещены. Когда прерывание запрещены, выполняем квант фоновой программы, когда разрешены — общаемся непосредственно с железом.
Т.е. взяли и просели по производительности минимум вдвое
0xd34df00d
Надо различать формализацию семантики языка и её реализацию.
Gryphon88
Не спорю. Но вот как положить предложенное решение на архитектуру и типовые требования — никак не соображу.
0xd34df00d
А как сейчас получается, что аппаратные прерывания ничего не прерывают посреди такта?
Gryphon88
Вход в прерывание блокирует все прочие. Когда прерывание обработано, начинают обрабатываться случившиеся во время обработки первого, в соответствие с таблицей приоритетов или живой очереди.
0xd34df00d
Ну вот я не вижу проблем с таким подходом. Не надо разрешать или запрещать прерывания на каждом чётном-нечётном такте, они разрешены как обычно, просто если прерывания не случилось, то вы считаете, что соответствующий шаг SSES ничего не поменял.
Gryphon88
Извините, не пояснил: пусть у нас два аппаратных таймера, у каждого зарегистрирован обработчик переполнения. Пока обрабатывается переполнение одного, второй будет продолжать тикать, инкрементируя значение в соответствующем регистре. Периферия независима от ядра и друг от друга.
0xd34df00d
С формализацией конкурентности порядком геморроя и без всяких микроконтроллеров и прочих аппаратных вещей, это совсем другой разговор.
axe_chita
Функция abort() вызывает немедленное прекращение программы. Очистка буферов файлов не производится. Функция возвращает вызывающему процессу значение 3 (обычно операционной системе).
Основное назначение функции abort() — это предотвращение закрытия файлов некорректно функционирующей программой.
Так что abort() это возможность ОС понять что пользовательская программа попала в «сумеречную зону»
0xd34df00d
Я знаю, что такое abort, но за справку спасибо.
abort не возвращает управление вызывающему коду. Кто там его вызвал — опять же, другой уровень абстракции.
axe_chita
Функция abort() это ситуация в программе как КФ «Бриллиантовая рука» Шеф, все пропало, всё пропало! Гипс снимают, клиент уезжает! т.е. форс-мажор. И в Си, насколько я помню, исключения (Try catch trow) не завезли?
И для ОС главное понимать что процесс неожиданно умер, а там уже другая логика, перезапустить процесс, сообщить куда надо об инциденте, перезагрузится, остановить исполнение.
axe_chita
И может вернуть код успеха или ошибки, или количество действительно прочитанных/записанных блоков
0xd34df00d
В том и проблема, что void в С и производных — костыль, а не полноценный тип. Попробуйте объявите значение типа void.
axe_chita
void — нет значения, пусто. И K&R через #define void int считал что он целое. скорее всего void первоначально был принять для оптимизации кодогенерации (чтоб не тратить время на передачу ненужного результата функции)
0xd34df00d
Если взять полноценный void, который действительно «нет значения», то функция, возвращающая такой void, означает лишь, что она никогда не возвращает управление (например, постоянно вызывая саму себя). А функция, которая принимает только void, на самом деле константна.
void, к которому мы так привыкли по сям, на самом деле населён каким-то единственным значением, которое мы, увы, не можем назвать (и это, может, нормально в С, но это имеет практические последствия в плюсах, в частности, в шаблонном коде).
Ну и, в конце концов, посмотрите, как записывается отрицание в интуиционистской логике, например.
axe_chita
из справочника
Так что вы пошли в какуюто эзотерику, ищите какойто полноценный void?
[Сарказм вкл]Давайте тогда искать полноценный int! Ну почему меня ограничивают какими-то рамками?
И почему только от -32768 до +32767?
Нет от -2147483648 до +2147483647 меня тоже категорически не устраивает, не по фэншую.
Вы что издеваетесь? Ваши -9,223,372,036,854,775,808 до 9,223,372,036,854,775,807 это такая мелочь в размерах вселенной.[Сарказм выкл]
Gryphon88
именно поэтому я предпочитаю uint8_t
axe_chita
;) наш человек, а то long, int, unsigned char понимаешь
0xd34df00d
Ну почему эзотерика, это всё было обнаружено, обосновано и рассмотрено задолго не то что до С, а даже до Алгола-60. И было бы это эзотерикой, не обсуждался бы сейчас в комитете C++ вопрос regular void.
Этот ваш void — это на самом деле unit type (забавно, там даже есть маленькая секция «Void type as unit type» как раз к нашему разговору).
Этот мой void — это действительно по-настоящему пустой тип, вроде такого, например.
axe_chita
Ну во первых void он не мой, а часть стандарта ANSI Си
И в вашей ссылке про unit type имеется следующий абзац
По моемому я это уже говорил несколько раз ранее.
И мы с вами обсуждаем не Idris который компилируется в набор промежуточных представлений, а из них — в си-код, при исполнении которого используется копирующий сборщик мусора с применением алгоритма Чейни, а язык Си. И кстати C!=C++, так что там плющилы обсуждают, насильников не касается.;)
BTW если Си так плох (не имеет истинного void) то почему IDRIS генерирует Си-код?
0xd34df00d
Ну так я могу в плюсах написать
А вот написать
я, увы, не могу (о чём и был мой исходный комментарий).
Насильников-то, может, и не касается, а вот вполне себе практические последствия оно вполне себе демонстрирует.
Потому что С-код генерировать удобно как промежуточное представление, это как LLVM IR, только без завязки на LLVM. Я бы понял такое возражение, если бы С был языком реализации, а не бекенда.
Да и это не единственный бекенд. Там в штатной поставке и JS есть, а вообще у идриса есть даже php-бекенд, но это же ни о чём не говорит. Напротив, сам автор того бекенда пишет «Many hosting providers, for example, still support PHP but don't necessarily allow you to write in anything nicer (maybe they'll let you write Node.js, but it's debatable how much nicer that is...)», хехе.
axe_chita
А зачем? В вашем коде return как минимум лишний, в заголовке и так описывается что функция не возвращает ничего. В строке void v; вы пропустили * перед v. Ну и в параметрах функции, если вы решили до конца исползовать void, вы забыли указать что ваша функция получает значение void.
Понимаете Си не объектно-ориентированный язык, он появился как ИНСТРУМЕНТАЛЬНЫЙ язык для написания ОС и приложений аж 46 лет назад. И авторы писали его как ИНСТРУМЕНТ, а не как средство описания ВСЕГО. Вообще нет плохих или хороших языков программирования, есть области где нужный язык НАИБОЛЕЕ предпочтителен. В конце концов компилятор переведет вашу программу или в ассемблер, или машкод, и процессору будет глубоко фиолетово как был сформирован этот код, вручную, ассемблером, прямым шитым кодом в Форте, интерпретатором Бейсика, компилятора Си или Паскаля и так далее. Язык программирования в первую очередь средство создания программ, а уж в какой парадигме вы желаете это делать, выбирать вам.
0xd34df00d
Таких функций очень мало математически.
Но я, в общем, понял, что математика — это ерунда, как оно там сделано в С — куда важнее.
axe_chita
exit(),abort(),clrscr() навскидку, и да Си не язык математики.
И вот такой кусок кода (да он страшно корявый, но всеже)
И мы язык программирования Си обсуждаем, с его сильными и слабыми сторонами, или математическую абстракцию?
Если вы желаете не ерундовую символьную математику, то посмотрите в сторону Лиспа, по крайней мере в Derive использовался диалект muLisp при обработке компьютерной алгебры.
Использование языка программирования Си часто приводит к написанию очень опасного кода. Но это не совсем справедливое обвинение. Такие проекты, как OpenBSD, показывают, что возможно писать безопасный код на Си. Проблема Си та же, что и в ассемблере — язык открывает вам не только все возможности архитектуры, но и кое-что ещё. Он дает все возможности для написания безопасного кода, но не делает эти вещи сам.
И давайте, не будем холиварить, у каждого языка есть своя ниша, и нет вины Си в том что он такой популярный, может он просто востребованный?
Мира вам!
Кому нравится поп, кому — попадья, а кому — попова дочка
vvzvlad
А если он одинаковый, то какай смысл подвигать его в качестве учебника? Можно сделать один и тот же код, который будет работать и на 8-битном МК и на Core i8 под виндой, но он же превратится в два кардинально разных бинарника, о каком понимании может идти речь?
Goron_Dekar
Да хватит брать экзальтированные и редкие платформы типа Core i8.
Легко сделать один и тот же код, который будет компиляться во что-то очень похожее на AVR и на PIC.
vvzvlad
Вообще я имел ввиду i5, опечатался. Не суть важно, хоть пентиум. Все равно различия какого-нибудь ARM и x86 таковы, что один и тот же код на си будет компилироваться в очень разный ассемблер.
dimaviolinist
AVR — фон Нейман.
PIC — Гарвард.
Там кардинально по разному будет компиляться. Для примера достаточно переключений страниц памяти на PICe.
И, судя по этому вашему комментарию, вы прекрасно это знаете.
P.S. Я как раз такой любитель МК, который старается считать такты в прерываниях :)
Sun-ami
AVR — это тоже Гарвард. Вот ARM — это фон Нейман.
axe_chita
Понимание в том что программе на Си необязательна операционная система, и она может управлять непосредственно лифтом © K&R
Бинарники (ассемблеры) разные, а вот логика программы одна, плюс Си мобильный и переносимый язык. К примеру есть такая замечательная книга Р.Берри, Б.Микинз «Язык Си Введение для программистов». Авторы книги предлагают изучить язык Си на примере транслятора RatC и приводят текст самого транслятора. Так вот этот транслятор мог генерировать код как для 8 разрядного процессора intel 8080, так и для 32 разрядного VAX. Более того компилятор мог выполнить самокомпиляцию, т.е. откомпилировать собственный код.
И в Квейке, между прочим, тоже был Си подобный QuakeC.
vvzvlad
Это сборище замечательных фактов, спасибо.
Только все-таки, как си позволяет понять принцип работы компьютера, если поднимает абстракцию от железа до такой степени, что становится переносимым?
Source
Никак :-)
Чтобы понять принцип работы компьютера, придётся поизучать хотя бы один ассемблер. На самом деле, это довольно увлекательное занятие.
axe_chita
Я и не утверждал что Си поможет вам понять принцип работы компьютера. Объяснить принцип работы ОС Си может практически. А что бы понять как работает компьютер (процессор) вам нужно разбираться с ассемблером, ну или в крайнем случае ознакомиться с Forth.
firedragon
C позволяет понять как работать с памятью. Чего автор по умыслу либо лукавству не озвучил.
Собственно понимание как работает память, это и есть тот рубеж который отделяет джуна от миддла.