なぜCプログラミングにはコンパイラが必要で、シェルスクリプトは必要ないのですか?


8

私はbashスクリプトを作成し、最初にコンパイルせずに実行しました。それは完全に機能しました。アクセス許可があってもなくても動作しますが、Cプログラムに関しては、ソースコードをコンパイルする必要があります。どうして?


9
これは、Cがコンパイル済み言語であり、Bashがインタープリター型言語であることを意味します。それ以上でもそれ以下でもありません。
Radon Rosborough、2016

10
私はこの質問をU&Lに関連していないため、トピック外として閉じることに投票します。それが関係しているとしても、満足のいく答えを提供せずに無限に議論することができるほど広範であり、 U&Lが問題を解決するための知識ベースであるという一般的な考え。
カウンター

4
@countermodeあなたは人々をスタック(交換|オーバーフロー)し、あなたのトリガーハッピーな閉票を行います。問題は幅広いものではありません。それは、非常に具体的な不足している理解の部分を明らかにします:コンパイルされた言語と解釈された言語の違い。これは、2つの段落で説明し、別の2つの段落でそれぞれの(不利な)利点を指摘します。さらに、Cとbashには異なる目標があることを要約する脚注があるため、異なるアプローチを選択しました。
mtraceur 2016

2
@mtraceur申し訳ありませんが、私は誰かを怒らせるつもりはありません。トリガーハッピークローズ投票の時点では、これは少し不公平です。質問を閉じるには5票が必要です。他の誰かが質問を閉じないように投票した場合は、問題ありません。unix.stackexchange.com/help/on-topicによると、この質問は完全に正しいですが、そのような質問がU&Lに属しているかどうかは わかりません
カウンター

2
@mtraceurは、広範ではないと思われる場合は同意しません。コンパイラー/インタープリター/その他の違いについては、多くのことがわかります。インタプリタ/コンパイル済み言語。ヒットしやすいキャバットがあります。さらに、この質問は「コンピューターサイエンス」または「プログラマー」SEに適しています。
MatthewRock 2016

回答:


23

つまり、シェルスクリプトはコンパイルされず、解釈されます。シェルはスクリプトを一度に1つのコマンドで解釈し、各コマンドの実行方法を毎回把握します。とにかく、他のプログラムの実行にほとんどの時間を費やしているので、これはシェルスクリプトにとって理にかなっています。

一方、Cプログラムは通常コンパイルされます。実行できるようになる前に、コンパイラーはプログラム全体をマシンコードに変換します。過去にはCインタープリター(Atari STのHiSoftのCインタープリターなど)がありましたが、非常に珍しいものでした。最近のCコンパイラは非常に高速です。TCCは非常に高速なので、それを使用して「Cスクリプト」作成できます。#!/usr/bin/tcc -runそのため、シェルスクリプトと同じように実行されるCプログラムを作成できます(ユーザーの観点から)。

一部の言語には一般的にインタープリターとコンパイラーの両方があります。BASICは思い浮かぶ一例です。

いわゆるシェルスクリプトコンパイラも見つけることができますが、私が見たものはラッパーを難読化しているだけです。スクリプトを実際に解釈するためにシェルを使用しています。mtraceurポイント適切なシェルスクリプトコンパイラは確かに非常に興味深いだけではなく、可能でしょうけれども。

これについての別の考え方は、シェルのスクリプト解釈機能は、コマンドライン処理機能の拡張であり、当然、解釈されたアプローチにつながると考えることです。一方、Cはスタンドアロンのバイナリを生成するように設計されています。これはコンパイルされたアプローチにつながります。通常コンパイルされる言語は、インタプリタ、または少なくともコマンドラインパーサ(REPL、read-eval-print loopsとして知られています。シェル自体がREPLです)も開発する傾向があります


1
スクリプト言語の多くの「コンパイラ」は単なるラッパーです。インタープリター実行可能ファイルを圧縮されたソースコードと連結するだけのBASICコンパイラーを覚えています。
Dmitry Grigoryev 2016

@DmitryGrigoryev私は簡単にそれを信じることができます!BASICでは、Turbo Basicのようなコンパイラを考えていました。DOS BATファイル用の本当のコンパイラがいくつかあったと思いますが、私は間違っているかもしれません!一般的なPコードアプローチもあります...
Stephen Kitt

1
シェルの「真の」コンパイラは完全に可能であることに注意してください。それは素朴な変種がちょうど一般の束を呼び出すプログラムを生成することから、一般的にちょうど努力/複雑価値はないexecveopenclosereadwrite、およびpipeいくつかが点在システムコール、getenvsetenv非エクスポート変数のために(、および内部ハッシュマップ/配列操作)、等Bourneシェルおよび誘導体は、プログラミングではない言語でもあり、そのコードの再順序付け、等のような低レベルのコンパイラの調整から限り恩恵
mtraceur

@StephenKittは、あなたの回答のコメントに関連して新しい質問を提起してもよろしいですか。あなたは答えますがたくさん説明します。しかし、私にとっても新しい質問を投げかけます。
Mongrel、2016

確かに、私は新しい質問を開きます。チャットで聞いて欲しいのではないかと思った。それはあなたの答えに関連しているからです。
Mongrel、2016

6

次のプログラムを検討してください。

2 Mars Bars
2 Milks
1 Bread
1 Corn Flakes

bashあなたは1を見たときにパンを認識することができ、「経験豊富な買い物客」と呼ばれる複雑なプログラムを実行されているための方法、あなたはなど、牛乳のためにこの作品を探してさまよう、その後、火星バーを探して店の周りにさまよい、最終的にはそれらを見つけますそして、ショッピングの他のすべての複雑さ。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「経験豊富な買い物客」であり、スクリプトを実行して、何もコンパイルせずにそれを実行できます。Cコンパイラーは、実行するためにもはや支援を必要としないスタンドアロンプ​​ログラムを生成します。

インタプリタとコンパイラには、それぞれ長所と短所があります。


3
素敵なアナロジー...異なるアーキテクチャ(sic!)で同じ買い物リストを使用できるが、同じマシンコードを使用できない理由も説明しています-すべてのものは異なる場所にあります。ただし、別の「経験豊富な買い物客」必要な場合もあります。別のスーパーマーケットを知っている人。
ピーター-モニカを復活させる

6

それはすべて、人間がコンピュータが理解する機械命令に変換されるときに読み書きできるプログラムの技術的な違いに起因します。各メソッドの異なる長所と短所が、一部の言語がコンパイラを必要とするように書かれている理由です、そしていくつかは解釈されるように書かれています。

まず、技術的な違い

(注:質問に対処するために、ここでは多くのことを簡略化しています。より深く理解するために、私の回答の下部にあるテクニカルノートでは、ここでいくつかの簡略化を詳しく説明/洗練しており、この回答に関するコメントにはいくつかの有用な説明と議論も..)

プログラミング言語には、基本的に2つの一般的なカテゴリがあります。

  1. 別のプログラム(「コンパイラー」)はプログラムを読み取り、コードが実行するステップを決定してから、それらのステップを実行する新しいプログラムをマシンコード(コンピューター自体が理解する「言語」)に書き込みます。
  2. 別のプログラム(「インタープリター」)はプログラムを読み取り、コードが実行するステップを決定し、それらのステップ自体を実行します。新しいプログラムは作成されません。

Cは最初のカテゴリにあります(C コンパイラはC 言語をコンピュータのマシンコードに変換します。マシンコードはファイルに保存され、そのマシンコードを実行すると、必要な処理が行われます)。

bashは2番目のカテゴリにあります(bash インタープリターはbash 言語を読み取り、bash インタープリターは必要な処理を実行します。つまり、「コンパイラーモジュール」自体はなく、インタープリターは解釈と実行を行いますが、コンパイラーは読み取りと翻訳を行います)。 。

これが何を意味するかは、すでにお気づきかもしれません。

Cでは、「解釈」ステップを1回実行し、プログラムを実行する必要があるときはいつでも、コンピューターにマシンコードを実行するように指示するだけです。コンピューターは、余分な「思考」を行う必要なく、直接実行することができます。

bashでは、プログラムを実行するたびに「解釈」ステップを実行する必要があります。コンピューターはbashインタープリターを実行しており、bashインタープリターは追加の「思考」を行って、毎回コマンドごとに実行する必要があることを理解します。 。

そのため、Cプログラムでは、準備(コンパイル手順)に必要なCPU、メモリ、時間が長くなりますが、実行にかかる時間と作業は少なくなります。bashプログラムは、準備に必要なCPU、メモリ、時間は少なくなりますが、実行するのにより多くの時間と作業が必要です。最近のコンピューターは非常に高速であるため、ほとんどの場合、これらの違いに気付かないでしょうが、違いはあります。その違いは、大きなプログラムや複雑なプログラム、または多くの小さなプログラムを実行する必要がある場合に加算されます。

また、Cプログラムはコンピューターのマシンコード(「母国語」)に変換されるため、プログラムを取得して別のコンピューターコード(たとえば、Intel 64ビットからIntel 32)で別のコンピューターにコピーすることはできません。 -bit、またはIntelからARMまたはMIPSなどへ)。あなたは、他の機械語のためにそれをコンパイルする時間を費やす必要が再び。ただし、bashプログラムは、bashインタープリターがインストールされている別のコンピューターに移動するだけで、問題なく実行できます。

今あなたの質問の理由の部分

Cのメーカーは、数十年前からハードウェア上でオペレーティングシステムやその他のプログラムを作成していたが、それは現代の標準ではかなり制限されていた。さまざまな理由から、プログラムをコンピューターのマシンコードに変換することが、当時の目標を達成するための最良の方法でした。さらに、彼らが書いたコードが効率的に実行されることが重要な種類の作業を行っていました。

そして、Bourneシェルとbashのメーカーは反対を望んでいました:彼らはすぐに実行できるプログラム/コマンドを書きたかったです-コマンドラインで、ターミナルで、あなたはただ1行、1つのコマンドを書き、それを持ちたいのです。実行します。また、シェルインタープリター/プログラムがインストールされている場所であればどこでも機能するように記述したスクリプトが必要でした。

結論

つまり、bashにはコンパイラは必要ありませんが、Cにはコンパイラが必要です。これらの言語は実際のコンピュータアクションに異なる方法で変換され、言語の目的が異なるため、それらの異なる方法が選択されました。

その他の技術的/高度な詳細/メモ

  1. 実際には、C インタープリターまたはbash コンパイラーを作成できます。。それが可能であることを妨げるものは何もありません。それは、それらの言語が異なる目的のために作成されたというだけです。多くの場合、複雑なプログラミング言語用の優れたインタープリターやコンパイラーを作成するよりも、単にプログラムを別の言語で書き直す方が簡単です。特に、それらの言語に得意な特定のものがあり、そもそも特定の方法で設計されている場合。Cはコンパイルするように設計されているため、対話型シェルでは必要な便利な省略形が多くありませんが、データ/メモリの非常に具体的で低レベルの操作を表現したり、オペレーティングシステムとやり取りしたりするのに非常に適しています。は、効率的にコンパイルされたコードを記述したいときによく行うタスクです。一方、bashは他のプログラムの実行が非常に得意です。

  2. より高度な詳細:実際には両方のタイプが混在するプログラミング言語があります(これらはソースコードを「ほとんどすべて」翻訳するため、ほとんどの解釈/「思考」を一度に実行でき、ほんの少ししか実行できません解釈/後の「思考」)。Java、Python、および他の多くの現代言語は、実際にはそのようなブレンドです。それらは、インタープリター型言語の移植性および/または迅速な開発の利点のいくつか、およびコンパイル済み言語の速度のいくつかを取得しようとします。そのようなアプローチを組み合わせるには多くの可能な方法があり、言語が異なればそれは異なります。このトピックを掘り下げたい場合は、「バイトコード」にコンパイルするプログラミング言語を読むことができます(これは、独自の「機械語」にコンパイルするようなものです)

  3. 実行ビットについて質問しました。実際、実行可能ビットは、そのファイルの実行が許可されていることをオペレーティングシステムに伝えるためだけにあります。実行権限なしでbashスクリプトが機能する唯一の理由は、実際にはbashシェル内からスクリプトを実行しているためだと思います。通常、オペレーティングシステムは、実行ビットが設定されていないファイルを実行するように要求されると、エラーを返します。しかし、bashのような一部のシェルでは、基本的にオペレーティングシステムが通常実行する手順をエミュレートすることで、エラーを確認し、とにかくファイルを実行します(ファイルの先頭にある「#!」行を調べて、ファイルを解釈するためにそのプログラムを実行します。デフォルトはそれ自体、または/bin/sh「#!」行がない場合)。

  4. コンパイラがシステムにすでにインストールされている場合もあれば、IDEに独自のコンパイラが付属している場合や、コンパイルが実行される場合もあります。これにより、コンパイルされた言語を非コンパイル言語と同じように使用できるようになりますが、技術的な違いはまだあります。

  5. 「コンパイルされた」言語は必ずしもマシンコードにコンパイルされるわけではなく、これをコンパイルする全体がそれ自体がトピックです。基本的に、この用語は広く使用されています。実際にはいくつかのことを指します。ある特定の意味では、「コンパイラ」は、ある言語(通常、人間が使用しやすい「高レベル」の言語)から別の言語(通常、コンピュータが使用しやすい「低レベル」の言語)へのトランスレータにすぎません-時には、実際にはそれほど頻繁ではありませんが、これはマシンコードです)。また、人々が「コンパイラ」と言うとき、彼らは実際に一緒に動作する複数のプログラムについて話している(典型的なCコンパイラの場合、それは実際には「プリプロセッサ」、コンパイラ自体、「アセンブラ」、および「リンカ")。


なぜこの質問が反対票を投じられたのか理解できません。これは、広範囲であまり明確ではない質問に対する印象的な包括的回答です。
Anthony Geoghegan 2016

コンパイラは、ある言語を別の言語に翻訳します。機械語にコンパイルする必要はありません。あなたはバイトコードコンパイラを持つことができます。JavaからASMへのコンパイラーなど。Cのメーカーはほとんどのパワーを望んでおらず、ニーズに合う言語を望んでいました。Cはある程度解釈できます。Bashはコンパイルできます-shcがあります。言語がコンパイル/解釈されるかどうか-ほとんどの場合-は一部の規則に従っていますが、言語自体ではなく、使用するツールに依存します。
MatthewRock 2016

@MatthewRock必要に応じてマシン言語ではないものへのコンパイルに対処するためのテクニカルノートを追加しました。私の最初のテクニカルノートは、「Cが解釈される... Bashがコンパイルされる可能性がある」ことをすでにカバーしていると思います。「Cのメーカーはほとんどのパワーを望んでいない」問題に対処する方法についてのアイデアがありますが、当時使用していたハードウェア(null-byte- as-string-terminatorの問題の一部は、これを行うと、結局、それらの文字列を反復処理するときに1つ少ないレジスタを使用できるようになったためです。
mtraceur 2016

@MatthewRock(続き)Cのメーカーは厳密にはパワーを求めていなかったと言っても差し支えないと思いますが、彼らはOS作成作業を抽象化したいと思っていました。そして、そうでなければアセンブラーで行われる作業でした。そのため、彼らは、コードを記述していたPDPマシンで使用されるマシンコードと密接に相関する言語を作成しました。これは、少なくとも注目すべき設計目標ではないにしても、そのプラットフォームでの効率に影響を与えました。素朴な非最適化コンパイラでも。
mtraceur 2016

彼らはアセンブラではそれをしません。彼らはBを持っていた。落とし穴がどこにでもあり、質問はここでは話題になりません。概して、答えはおそらく質問に答えるでしょうが、正確ではない詳細がまだあります。
MatthewRock 2016

5

プログラミング/スクリプト言語は、コンパイルまたは解釈できます。

コンパイルされた実行可能ファイルは常に高速で、実行前に多くのエラーを検出できます。

解釈された言語は通常、記述が簡単で、コンパイルされた言語ほど厳密ではないため適応が容易であり、配布するのを容易にするコンパイルを必要としません。


1
bashスクリプトでも、実行時にエラーが発生します。コンパイルはしません。
Mongrel

30
常に危険な言葉です...
ラドンロスバラ

3
最適化されたコンパイルCPythonのは、多くの場合、より遅い最適化asm.js(はJavaScriptのサブセット)を超えています。したがって、高速ではない例があり、したがって「常に」高速ではありません。ただし、通常ははるかに高速です。
wizzwizz4 2016

2
これは質問の答えにはなりません。
mathreadler '15 / 12/16

3
常に速いというのは大胆な主張です。しかし、これはコンパイラとインタプリタの理論(および定義)に深く関わっています。
Giacomo Catenazzi 2016

3

英語があなたの母国語ではないことを想像してください(英語が母国語でない場合、それはあなたにとって非常に簡単かもしれません)。

これを読む方法は3つあります。

  1. (通訳)あなたが読んでいるとき、それを見るたびに各単語を翻訳する
  2. (最適化された解釈済み)一般的なフレーズ(「あなたの母国語」など)を見つけて翻訳し、書き留めます。次に、各単語を翻訳します-すでに翻訳したフレーズを除きます
  3. (コンパイル済み)他の誰かに回答全体を翻訳するよう依頼する

コンピュータには、ある種の「母国語」があります。これは、プロセッサが理解する命令と、オペレーティングシステム(Windows、Linux、OSXなど)が理解する命令の組み合わせです。この言語は人間には読めません。

Bashなどのスクリプト言語は、通常、カテゴリ1と2に分類されます。これらは一度に1行ずつ取得し、その行を翻訳して実行してから、次の行に進みます。MacとLinuxでは、Bash、Python、Perlなどのさまざまな言語用に、かなりの数の異なるインタープリターがデフォルトでインストールされています。Windowsでは、これらを自分でインストールする必要があります。

多くのスクリプト言語は少し前処理を行います-頻繁に実行されるか、そうでなければアプリケーションを遅くするコードのチャンクをコンパイルすることによって実行をスピードアップしようとします。あなたが耳にするかもしれないいくつかの用語には、時間先行(AOT)またはジャストインタイム(JIT)コンパイルが含まれます。

最後に、Cのようなコンパイル済み言語は、プログラムを実行する前にプログラム全体を翻訳します。これには、実行とは異なるマシンで変換を実行できるという利点があります。そのため、プログラムをユーザーに提供するときに、まだバグがある可能性がある場合でも、いくつかのタイプのエラーはすでにクリーンアップできます。これを翻訳者に渡した場合と同じようgarboola mizene resplunksに、がどのように有効な英語のように見えるかについてお話ししますが、翻訳者は私がナンセンスな話をしていることを教えてくれます。コンパイルされたプログラムを実行する場合、インタプリタは必要ありません-すでにコンピュータの母国語です

ただし、コンパイルされた言語には1つの欠点があります。コンピューターには、ハードウェアとオペレーティングシステムの機能で構成された母国語があると述べました。まあ、Windowsでプログラムをコンパイルした場合、コンパイルされたプログラムが動作することは期待できません。 Mac。一部の言語は、Pidgin Englishのような中途半端な言語にコンパイルすることでこの問題を回避します。これにより、コンパイルされた言語の利点とわずかな速度の向上を得ることができますが、バンドルする必要があります。コード付きのインタープリター(またはすでにインストールされているものを使用)。

最後に、IDEはおそらくファイルをコンパイルしていて、コードを実行する前にエラーについて通知することができます。場合によっては、このエラーチェックはコンパイラが行うよりも詳細になることがあります。多くの場合、コンパイラーは必要な量だけをチェックして、適切なネイティブコードを生成できるようにします。IDEは多くの場合、いくつかの追加チェックを実行し、たとえば、変数を2回定義したかどうか、または使用していないものをインポートしたかどうかを通知します。


この答えは素晴らしいですが、Perlの「インタプリタ」が使用する動的コンパイルは、通常「JIT」と呼ばれるものとは異なるため、おそらくこの用語を避けるのが最善です。JITは通常、すでにコンパイルされたバイトコードからターゲットマシンコード(JVM、.Net CLRなど)へのジャストインタイムコンパイルを指すために使用されます。
IMSoP 2016

@IMSoPええ、バイトコンパイルを行う言語と、バイトコードからJITをコンパイルする言語は、実際には異なります。言及する価値があると思います(私の回答の脚注に簡単な参照を付けました)が、JITが陥る一般的な考え方は、「コンパイル済み」の中間のどこかにある可能性があるということです。 「解釈済み」、つまり部分的にコンパイルされ、部分的に解釈されます。とは言っても、それがOPのようにコンパイル済み/解釈済みの違いをまだ理解していない人にとって、それがより価値があるのか​​、混乱/混乱させるのかはわかりません。
mtraceur 2016

@mtraceur正直なところ、たとえば、PHP 7、Perl 5、.Net、Javaのモデルの違いに少し迷ってしまいます。おそらく、初心者にとって最良の要約は、「中間表現の使用、プログラムの実行に応じたチャンクのコンパイルまたは再コンパイルなど、コンパイルと解釈を混合するさまざまな方法がある」です。
IMSoP 2016

@IMSoP同意する。それは私が私の回答で取ったアプローチであり、あなたのこのコメントは私にそれをさらに洗練する方法についてのアイデアを与えました。ありがとうございます。
mtraceur 2016

@IMSoP JITはバイトコードで発生するだけではありません。たとえば、node.jsにはいくつかのJIT機能があります。しかし、私は同意します-初心者の質問のように感じたので、単純化のためにすべてを「JIT」というバナーの下にまとめました-より単純な用語を使用するように編集しています。
user208769

1

多くの人が解釈とコンパイルについて話していますが、一部の解釈言語は実際に実行前に中間バイトコードにコンパイルされるため、よく見るとこれは少し誤解を招く可能性があると思います。

結局、Cプログラムを実行可能形式にコンパイルする必要がある本当の理由は、Cソースファイルのコードを実行可能なものに変換するためにコンピューターが多くの作業を行う必要があるため、製品を保存するのに意味があるからです。これらすべてが実行可能ファイルで機能するため、プログラムを実行するたびに再度実行する必要はありません。

一方、シェルインタープリターは、シェルスクリプトを「機械操作」に変換するためにほとんど作業を行う必要はありません。基本的には、スクリプトを1行ずつ読み取り、空白で分割し、ファイルのリダイレクトとパイプラインを設定して、fork + execを実行するだけです。シェルスクリプトのテキスト入力を解析および処理するオーバーヘッドは、シェルスクリプトでプロセスを起動するのにかかる時間と比較して非常に小さいため、単に解釈するのではなく、中間スクリプト形式でシェルスクリプトをコンパイルするのはやり過ぎです。ソースコードを直接。


+1、これは「馬の前にカートを置く」のではないかと思いますが、おそらく元のシェルは最初はインタラクティブに使用できるように設計されていたため、コンパイルを邪魔しないようにオーバーヘッドが少なく、比較的単純でした。それに基づいた設計上の決定だけですか?
mtraceur 2016

はい、それはこの質問を見る別の方法です:)
hugomg
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.