Зачем программированию на С нужен компилятор, а сценариям оболочки нет?

8

Я написал скрипт bash и выполнил его без предварительной компиляции. Это сработало отлично. Он может работать с разрешениями или без них, но когда речь идет о программах на C, нам нужно скомпилировать исходный код. Почему?

дворняжка
источник
9
Это означает, что C - это скомпилированный язык, а Bash - интерпретируемый язык. Ни больше ни меньше.
Радон Росборо
10
Я голосую, чтобы закрыть этот вопрос как не по теме, потому что он не имеет отношения к U & L, и даже если бы он был, он слишком широк, так как его можно бесконечно обсуждать, не предоставляя удовлетворительного ответа, не говоря об ответе, который соответствует Общая идея U & L является базой знаний для решения проблем.
контррежим
4
@countermode Вы складываете (обмениваете | переполняете) людей, и ваши довольные триггеры закрывают голоса. Вопрос не является широким: он обнаруживает совершенно конкретную недостающую часть понимания: разницу между компилируемыми и интерпретируемыми языками. Для объяснения требуются пара абзацев и пара абзацев, чтобы указать на преимущества каждого из них, а также сноска, в которой резюмируется, что C и bash преследовали разные цели, поэтому они выбрали разные подходы.
mtraceur
2
@mtraceur Извините, я не хочу никого обидеть. Что касается довольных триггерами близких голосов, это немного несправедливо. Для закрытия вопроса требуется пять голосов, и если кто-то еще проголосует, чтобы не закрыть его, я буду в порядке. Вы абсолютно правы в этом вопросе, но я не уверен, принадлежат ли такие вопросы U & L согласно unix.stackexchange.com/help/on-topic .
контррежим
2
@mtraceur, если вы думаете, что это не широкое, я не согласен с вами. Существует много сказать о разнице между компилятором / интерпретатором / и т. Д. и интерпретируемые / скомпилированные языки. Есть кеваты, в которые легко попасть. Более того, этот вопрос лучше подходит для «компьютерных наук» или «программистов» SE.
MatthewRock

Ответы:

23

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

С другой стороны, программы на Си обычно компилируются: перед тем, как их можно будет запустить, компилятор преобразует их в машинный код полностью, раз и навсегда. В прошлом были C-интерпретаторы (такие как C-интерпретатор HiSoft на Atari ST), но они были очень необычными. В настоящее время компиляторы C очень быстрые; TCC настолько быстр, что вы можете использовать его для создания «C-сценариев», используя #!/usr/bin/tcc -runшебанг, так что вы можете создавать C-программы, которые работают так же, как сценарии оболочки (с точки зрения пользователей).

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

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

Еще один способ думать об этом - учитывать, что возможность интерпретации сценариев оболочки является расширением ее возможностей обработки командной строки, что, естественно, приводит к интерпретируемому подходу. C, с другой стороны, был разработан для производства автономных двоичных файлов; это приводит к скомпилированному подходу. Языки, которые обычно компилируются, тоже имеют тенденцию к появлению интерпретаторов или, по крайней мере, анализаторов командной строки (известные как REPLs, циклы read-eval-print ; оболочка сама по себе является REPL).

Стивен Китт
источник
1
Многие «компиляторы» для языков сценариев являются просто оболочками. Я помню BASIC-компилятор, который просто объединял исполняемый файл интерпретатора с заархивированным исходным кодом.
Дмитрий Григорьев
@DmitryGrigoryev Я легко могу в это поверить! Для BASIC я думал о компиляторах, таких как Turbo Basic. Я думаю, что были некоторые настоящие компиляторы для файлов DOS BAT, но я могу ошибаться! Есть также общий подход с использованием p-кода ...
Стивен Китт
1
Обратите внимание, что «истинный» компилятор для оболочки вполне возможен. Это просто вообще не стоит усилий / сложностей, так как наивный вариант будет производить только программу , которая обычно вызывает кучу execve, open, close, read, write, и pipeсистемные вызовы, вперемешку с некоторыми getenv, setenvи операциями внутреннего HashMap / массива (для неэкспортированных переменных ) и т. д. Оболочка Bourne и ее производные также не являются языками программирования, которые в такой же степени получают выгоду от низкоуровневых настроек компилятора, таких как переупорядочение кода и т. д.
mtraceur
@StephenKitt не возражаете, если я задам новый вопрос, касающийся комментария к вашему ответу. Хотя ты отвечаешь многое объясняешь. но также поднимает новый вопрос для меня.
Дворняга
Конечно, я бы открыл новый вопрос. Я подумал, что вы можете попросить меня об этом в чате. потому что это относится к вашему ответу.
Дворняга
6

Рассмотрим следующую программу:

2 Mars Bars
2 Milks
1 Bread
1 Corn Flakes

Таким bashобразом, вы бродите по магазину в поисках баров Марс, наконец, находите их, затем бродите вокруг в поисках молока и т. Д. Это работает, потому что вы запускаете сложную программу под названием «Опытный покупатель», которая может распознать хлеб, когда вы его видите. и все остальные сложности покупок. bashэто довольно сложная программа.

Кроме того, вы можете передать свой список покупок компилятору покупок. Компилятор немного подумает и выдаст вам новый список. Этот список длинный , но состоит из гораздо более простых инструкций:

... lots of instructions on how to get to the store, get a shopping cart etc.
move west one aisle.
move north two sections.
move hand to shelf three.
grab object.
move hand to shopping cart.
release object.
... and so on and so forth.

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

Это отдельная программа, и для ее выполнения не требуется «Опытный покупатель». Все, что ему нужно - это человек с «Базовой человеческой операционной системой».

Возвращаясь к компьютерным программам: bashэто «Опытный покупатель» и может взять сценарий и просто сделать это, ничего не компилируя. Компилятор AC создает автономную программу, которая больше не нуждается в какой-либо помощи для запуска.

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

Стиг Хеммер
источник
3
Хорошая аналогия ... также объясняет , почему вы можете использовать один и тот же список покупок , но не тот же машинный код на другой архитектуре (так!) - все вещи в разных местах и т.д. Вы можете нужен другой «опытный покупатель» , хотя кто знает другой супермаркет.
Питер - Восстановить Монику
6

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

Во-первых, техническая разница

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

Есть в основном две основные категории языков программирования:

  1. Другая программа («компилятор») читает вашу программу, определяет, какие шаги должен выполнить ваш код, а затем записывает новую программу в машинный код («язык», который понимает ваш компьютер), который выполняет эти шаги.
  2. Другая программа («интерпретатор») читает вашу программу, определяет, какие шаги должен выполнить ваш код, а затем выполняет эти шаги сама . Новая программа не создана.

C относится к первой категории ( компилятор C переводит язык C в машинный код вашего компьютера : машинный код сохраняется в файл, а затем, когда вы запускаете этот машинный код, он делает то, что вы хотите).

bash относится ко второй категории ( интерпретатор bash читает язык bash, а интерпретатор bash делает то, что вы хотите: поэтому сам по себе «модуль компилятора» не работает, интерпретатор выполняет интерпретацию и выполнение, тогда как компилятор выполняет чтение и перевод) ,

Возможно, вы уже заметили, что это значит:

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

С bash вы должны выполнять шаг «интерпретировать» каждый раз, когда запускаете программу - на вашем компьютере запускается интерпретатор bash, а интерпретатор bash делает дополнительные «размышления», чтобы выяснить, что нужно делать для каждой команды, каждый раз ,

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

Кроме того, поскольку программы на C преобразуются в машинный код («родной язык») компьютера, вы не можете взять программу и скопировать ее на другой компьютер с другим машинным кодом (например, Intel 64-bit на Intel 32). -бит или от Intel до ARM или MIPS или что-то еще). Вы должны тратить время , чтобы собрать его для этого другого машинного языка снова . Но программу bash можно просто перенести на другой компьютер, на котором установлен интерпретатор bash, и она будет работать нормально.

Теперь почему часть вашего вопроса

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

И создатели оболочки Bourne и bash хотели обратного: они хотели писать программы / команды, которые могли бы быть выполнены немедленно - в командной строке, в терминале, вы хотите просто написать одну строку, одну команду и получить ее. выполнить. И они хотели, чтобы скрипты, которые вы написали, работали везде, где у вас был установлен интерпретатор / программа оболочки.

Вывод

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

Другие технические / расширенные детали / примечания

  1. Вы действительно можете создать интерпретатор C или bash- компилятор, Ничто не мешает этому быть возможным: просто эти языки созданы для разных целей. Часто проще просто переписать программу на другом языке, чем написать хороший интерпретатор или компилятор для сложного языка программирования. Особенно, когда у этих языков есть определенная вещь, в которой они были хороши, и были разработаны с определенным способом работы в первую очередь. C был разработан для компиляции, поэтому в интерактивной оболочке отсутствует множество удобных сокращений, которые вы хотели бы использовать, но он очень хорош для выражения очень специфических низкоуровневых манипуляций с данными / памятью и взаимодействия с операционной системой. , которые вы часто выполняете, когда хотите написать эффективно скомпилированный код. Между тем, Bash очень хорошо выполняет другие программы,

  2. Более подробные сведения: на самом деле существуют языки программирования, которые представляют собой смесь обоих типов (они переводят исходный код «в большинстве случаев»), так что они могут выполнить большую часть интерпретации / «мышления» один раз и выполнить лишь немного интерпретации / «мышления» позже). Java, Python и многие другие современные языки на самом деле являются такими смесями: они пытаются дать вам некоторые преимущества переносимости и / или быстрой разработки интерпретируемых языков, а также некоторую скорость скомпилированных языков. Есть много возможных способов комбинировать такие подходы, и разные языки делают это по-разному. Если вы хотите углубиться в эту тему, вы можете прочитать о языках программирования, компилируемых в «байт-код» (что похоже на компиляцию в ваш собственный искусный «машинный язык»).

  3. Вы спросили о бите выполнения: на самом деле, исполняемый бит только там, чтобы сообщить операционной системе, что этот файл разрешен для выполнения. Я подозреваю, что единственная причина, по которой скрипты bash работают для вас без разрешения на выполнение, - это то, что вы запускаете их из оболочки bash. Обычно операционная система, когда ее просят выполнить файл без установленного бита выполнения, просто возвращает ошибку. Но некоторые оболочки, такие как bash, увидят эту ошибку и в любом случае возьмут на себя задачу запустить файл, эмулируя в основном шаги, которые обычно делает операционная система (посмотрите строку «#!» В начале файла и попробуйте выполнить эту программу для интерпретации файла, по умолчанию либо сам, либо, /bin/shесли нет строки "#!").

  4. Иногда компилятор уже установлен в вашей системе, а иногда IDE поставляются со своим собственным компилятором и / или запускают компиляцию за вас. Это может заставить скомпилированный язык «чувствовать» себя как некомпилированный язык для использования, но техническое отличие все еще есть.

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

mtraceur
источник
Я не понимаю, почему этот вопрос был отклонен. Это впечатляюще исчерпывающий ответ на широкий и не очень понятный вопрос.
Энтони Геогеган
Компилятор переводит один язык на другой. Он не должен компилироваться в машинный язык. Вы можете иметь компилятор байт-кода. Компилятор с Java на ASM и т. Д. Создатели языка C не хотели больше работать, им нужен язык, который соответствовал бы их потребностям. С можно интерпретировать в некоторой степени. Bash может быть скомпилирован - есть щ . То, будет ли язык компилироваться / интерпретироваться - в большинстве случаев - зависит от используемых вами инструментов, а не от самого языка, хотя соблюдаются некоторые соглашения.
MatthewRock
@MatthewRock Я добавил техническую заметку, касающуюся компиляции, но не обязательно на машинном языке. Я чувствую, что моя первая техническая заметка уже охватывает тему "C может быть интерпретирован ... Bash может быть скомпилирован". У меня есть идея, как решить проблему «создатели C не хотят больше всего мощности», хотя я думаю, что совершенно ясно, что они разработали язык так, чтобы он был эффективным на оборудовании, которое они использовали в то время (нулевой байт. Функция as-string-terminator была отчасти из-за того, что это позволило вам использовать на один регистр меньше, в конце концов, итерируя по ним строку).
mtraceur 15.12.16
@ MatthewRock (продолжение) Я думаю, что было бы справедливо сказать, что создатели C не совсем нуждались в мощности, но они действительно хотели абстрагировать работу по написанию ОС, что особенно важно в те дни, когда эффективность кода была ценна. И это была работа, которую они иначе сделали бы на ассемблере. Таким образом, они создали язык, тесно связанный с машинным кодом, используемым машинами PDP, для которых они писали свой код, что, по крайней мере, в качестве побочного эффекта, если не в качестве заметной цели проектирования, обеспечивало эффективность на этой платформе, даже с наивными неоптимизирующими компиляторами.
mtraceur 15.12.16
Они не будут делать это на ассемблере. Они имели B . Подводные камни везде, и вопрос здесь не по теме. В целом ответ, вероятно, ответит на вопрос, но все еще есть детали, которые не точны.
MatthewRock
5

Языки программирования / написания сценариев могут быть скомпилированы или интерпретированы.

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

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

Джули Пеллетье
источник
1
В скрипте bash также выдает ошибку при выполнении. но мы не компилируем это.
Дворняга
30
всегда опасное слово ...
Радон Росборо
3
Оптимизированный скомпилированный CPython часто медленнее, чем оптимизированный asm.js (подмножество JavaScript). Поэтому есть пример того, что он не быстрее, и поэтому он не всегда быстрее. Тем не менее, это обычно намного, намного быстрее.
wizzwizz4
2
Это не отвечает на вопрос.
mathreadler
3
всегда быстрее смелое требование. Но это слишком глубоко в теории компилятора и интерпретатора (и определения).
Джакомо Катенацци
3

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

Есть три способа прочитать это:

  1. (Интерпретировано) Когда вы читаете, переводите каждое слово каждый раз, когда вы его видите
  2. (Оптимизированный-интерпретированный) Найдите общие фразы (например, «ваш родной язык»), переведите их и запишите. Затем переведите каждое слово - кроме фраз, которые вы уже перевели
  3. (Составлено) Попросите кого-нибудь еще перевести весь ответ

Компьютеры имеют своего рода «родной язык» - комбинацию инструкций, которые понимает процессор, и инструкций, которые понимает операционная система (например, Windows, Linux, OSX и т. Д.). Этот язык не читается людьми.

Языки сценариев, такие как Bash, обычно попадают в категории 1 и 2. Они принимают строку по очереди, переводят эту строку и запускают ее, а затем переходят к следующей строке. В Mac и Linux по умолчанию установлено несколько разных интерпретаторов для разных языков, таких как Bash, Python и Perl. В Windows вы должны установить их самостоятельно.

Многие языки сценариев выполняют небольшую предварительную обработку - стараются ускорить выполнение, компилируя куски кода, которые будут часто выполняться или которые в противном случае замедлили бы работу приложения. Некоторые термины, о которых вы, возможно, слышали, включают компиляцию «опережающее время» (AOT) или «точное время» (JIT).

Наконец, скомпилированные языки, такие как C, переводят всю программу до того, как вы сможете их запустить. Преимущество этого заключается в том, что перевод может быть выполнен на другой машине по сравнению с выполнением, поэтому, когда вы даете программу пользователю, в то время как все еще могут быть ошибки, несколько типов ошибок уже могут быть устранены. Точно так же, как если бы вы дали это своему переводчику, и я упомянул, как garboola mizene resplunksэто может выглядеть для вас как действительный английский, но переводчик может сказать вам, что я говорю чепуху. Когда вы запускаете скомпилированную программу, ей не нужен переводчик - она ​​уже на родном языке компьютера

Однако у компилируемых языков есть один недостаток: я упомянул, что на компьютерах есть родной язык, состоящий из функций аппаратного обеспечения и операционной системы. Что ж, если вы компилируете свою программу в Windows, вы не ожидаете, что скомпилированная программа будет работать на Mac. Некоторые языки обходят это путем компиляции на некий промежуточный язык - немного похожий на Pidgin English - таким образом, вы получаете преимущества скомпилированного языка, а также небольшое увеличение скорости, но это означает, что вам нужно объединить интерпретатор с вашим кодом (или используйте тот, который уже установлен).

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

user208769
источник
Этот ответ великолепен, но я думаю, что динамическая компиляция, используемая Perl-интерпретатором, отличается от того, что обычно называется «JIT», поэтому, вероятно, лучше избегать этого термина. JIT обычно используется для ссылки на своевременную компиляцию из уже скомпилированного байтового кода в целевой машинный код, например, JVM, .Net CLR.
IMSoP
@IMSoP Да, языки, которые байтово компилируются, и языки, которые JIT компилируют из байтового кода, действительно разные вещи. Я думаю, что, возможно, стоит упомянуть (я вставил краткую ссылку на это в сносках моего ответа), но общая идея, что JIT относится к тому, что я думаю, стоит иметь в том, что возможно быть где-то посередине между «скомпилированным» и «интерпретируется», то есть частично компилируется и частично интерпретируется. Тем не менее, я не уверен, что это более ценно или сбивает с толку / отвлекает для кого-то, кто еще не понимает различия между скомпилированным / интерпретируемым, как OP.
mtraceur
@mtraceur Если честно, даже я немного теряюсь в различии между моделями, скажем, PHP 7, Perl 5, .Net и Java. Возможно, лучшее резюме для начинающего: «Существуют различные способы смешивания компиляции и интерпретации, включая использование промежуточного представления, а также компиляцию или повторную компиляцию фрагментов во время работы программы».
IMSoP
@IMSoP Я согласен. Это своего рода подход, который я использовал в своем ответе, и этот ваш комментарий дал мне идею о том, как уточнить это дальше. Так что спасибо тебе.
mtraceur
@IMSoP JIT происходит не только с байт-кодом - например, node.js имеет некоторые функции JIT. Но я согласен - для простоты я поместил их все под баннером «JIT», так как это было похоже на вопрос новичка - я редактирую его, чтобы использовать более простой термин.
user208769
1

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

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

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

hugomg
источник
+1, хотя я действительно задаюсь вопросом, не является ли это «помещением телеги перед лошадью»: возможно, первоначальные оболочки были изначально предназначены для интерактивного использования и, таким образом, с минимальными накладными расходами, чтобы не мешать компиляции, а относительная простота была только дизайнерское решение, основанное на этом?
mtraceur
Да, это еще один способ взглянуть на этот вопрос :)
hugomg