Cはプログラミングの背後にある原則を学ぶのに適した言語だと思います。Rubyのような高レベルな言語から「魔法のように」なっている低レベルの言語で何を学べると思いますか?
Cはプログラミングの背後にある原則を学ぶのに適した言語だと思います。Rubyのような高レベルな言語から「魔法のように」なっている低レベルの言語で何を学べると思いますか?
回答:
Cに存在し、高レベル言語にも存在しない原則は、コンピューターサイエンスの一般的な抽象的な意味ではありません。コンピューターサイエンスはすべてアルゴリズムに要約されており、すべてのアルゴリズムはCのようにチューリング完全な任意の言語で実装できます。
Cが高レベル言語から離れていることの違いは、マシンコードがCから離れていることの違いに似ています。マシンとコードの関係。
高級言語でコードを書くとき、コードがマシンとどのように相互作用するかについて(一般的に)気にする必要はありません。言語自体が定義する仮想マシンは、コード実行のこれらの側面の多くを隠します。
Cでは、プログラムとメモリの相互作用は最前線にあります。単にヒープの使用を管理する必要があるだけでなく、スタックとのコードの相互作用、コードのメモリへのアクセスがコードの動作とパフォーマンスにどのように影響するか(メモリアクセスの順序さえも)が含まれます間違った時間に間違ったメモリを読み取ると、パフォーマンスが効果的に低下する可能性があるため、注意を逸らすことができます。
高レベル言語では、これらのことは単純ではありません。メモリは、ユーザーの知らないうちに、場合によってはプロンプトなしで割り当ておよび割り当て解除されます。ほとんどの場合、これは単純に制御できません。ほとんどのメモリ割り当ての時期、場所、方法、および理由は、単にあなたから隠されています。
同様に、マシンコードまたはアセンブリコードを記述すると、さらに詳細が前面に表示されます。ほとんど何もあなたの視野の外に残されません。コードは、すべての割り当て、すべてのリソース、通過するすべてのデータを認識して書かなければなりませんCPUのレジスタを介して-高度な言語から難解なほどに除去された知識。
Cはプログラミングの背後にある原則を学ぶのに適した言語であることを知っています。
同意しません。Cには、プログラミングの背後にある原理を学ぶにはあまりにも多くの機能がありません。抽象化を作成するためのCの機能はひどく、抽象化はプログラミングの背後にある重要な原則の1つです。
ハードウェアがどのように機能するのか、したがって機械に対する機械的な同情を知りたい場合は、機械語(命令セットアーキテクチャとも呼ばれます)を学習し、最新のCPUのキャッシュ構成も学習する必要があります。アセンブリ言語はお勧めしません。ハードウェアの指示を理解するだけなので、コンパイラが生成するものを理解できます。
プログラミングの原則を学びたい場合は、Java、C#、Swiftなどの最新の言語、またはRustなどの他の多数の言語を使用してください。また、関数型を含むさまざまな種類のプログラミングパラダイムを研究します。
ほとんどのプログラミング言語は、抽象マシンの観点から説明されています。次に、コンパイラー、リンカー、アセンブラー、インタープリター、静的アナライザー、中間言語、ハードウェアなどのツールのセットを使用して実装され、プログラムによって観察されるように、少なくとも抽象マシンの予想されるすべての動作を尊重する結果をまとめて生成します。
Cは上記の規則の例外ではありません。それは、実際のハードウェアの概念を持たない抽象的なマシンの観点から説明されています。
そのため、Cがコンピューターの実際の動作を教えると人々が言うとき、彼らが通常意味するのは、CがCの動作を教えることです。しかし、Cはシステムプログラミングに非常に普及しているため、多くの人がCをコンピューター自体と混同し始めることは理解できます。そして、個人的には、Cがどのように機能するかを知ることは、コンピューター自体がどのように機能するかを知ることよりも重要であると言っているほどです。
それでも、Cとコンピューターは異なるものです。実際のハードウェアは実際には複雑です-C仕様を子供向けの本のように読む方法で。ハードウェアの動作に興味がある場合は、いつでもマニュアルを検索して、アセンブラーでコードの記述を開始できます。または、自分でハードウェアを設計できるように、いつでもデジタル回路について学び始めることができます。(少なくとも、Cのレベルが高いことに感謝します。)
さて、実際にハードウェアについて学ぶにはC以外のことも含まれます。しかし、Cは今日プログラマーに何か他のものを教えることができますか?
状況によると思います。
これらの可能性のいずれかを選択するのに速すぎないでください。私は長年コードを書いてきましたが、どちらが正解なのか、正解が2つのうちの1つなのか、この問題に関して正解のようなものがあるのかはまだわかりません。
おそらく最終的に両方のオプションを、説明した順序で大まかに適用する必要があると私はやや信じています。しかし、これは本当に技術的な問題ではないと思います。ほとんど教育的なものだと思います。すべての人が著しく異なる方法で学習するようです。
少なくとも私が提案した2番目のオプションを使用して上記の質問に答えた場合は、すでにいくつかの答えがあります。高級言語で学習できるものはすべて、CまたはミックスにCを追加することによる最小の拡張。
しかし、あなたの答えに関係なく、Cからほとんど独占的に学べることは確かにいくつかあります(そしておそらく他のいくつかの言語)。
Cは歴史的に重要です。私たちがどこから来たのかを見て理解することができ、おそらく私たちがどこに行くのかについてもう少しコンテキストを得ることができるマイルストーンです。特定の制限が存在する理由を理解でき、特定の制限が解除されたことを理解できます。
Cは、安全でない環境で作業することを教えることができます。言い換えれば、言語(任意の言語)が何らかの理由であなたのためにそれを行うことができない、またはできないときにあなたの背中を見るように訓練することができます。これにより、安全な環境でも優れたプログラマを作成できます。自分でバグを減らすことができ、安全でないプログラムから余分な速度を絞るために一時的に安全をオフにできるからです(ポインタの使用など) C#では)、安全性にランタイムコストが伴う場合。
Cは、すべてのオブジェクトにストレージ要件、メモリレイアウト、有限のアドレス空間を介してメモリにアクセスできるという事実などがあることを教えることができます。他の言語ではこれらの問題に注意を払う必要はありませんが、いくつかのケースでは、より多くの情報に基づいた決定を下すのに役立ついくつかの直観があります。
Cは、ビルドシステムを通じて、リンケージおよびオブジェクトファイルの詳細やその他の複雑さについて学習できます。これにより、ネイティブにコンパイルされたプログラムがソースコードから実行までにどのように行われるかについて、実践的な理解を深めることができます。
Cは、未定義の振る舞いの概念を通して、新しい方法で考えるように心を曲げることができます。未定義の動作は、ソフトウェア開発における私のお気に入りの概念の1つです。非古典的なコンパイラに対する影響の研究は、他の言語では得られないユニークなメンタルエクササイズだからです。ただし、この側面を十分に理解するには、試行錯誤を拒否し、慎重かつ慎重に言語の学習を開始する必要があります。
しかし、おそらくCが小さな言語であるためにあなたに与えることができる最も重要な実現は、すべてのプログラミングがデータと操作に要約されるという考えです。階層と仮想ディスパッチを備えたインターフェイスを備えたモジュールクラス、または純粋な数学関数を使用して操作されるエレガントで不変の値として、物事を見ることができます。そしてそれはすべて大丈夫です-しかし、Cはそれがすべて単なるデータ+操作であることを思い出させます。精神的な障壁をかなり減らすことができるので、これは便利な考え方です。
Cが学習に適しているのは、Cが原則を教えているからではありません。それは物事がどのように、あなたを教えて働きます。
Cは、運転するために建てられた70年代または80年代の古き良き自動車の1つと比較できます。それらを引き裂き、ねじごとにねじって、各部分がどのように機能するか、そしてそれがあなたの手で見ることができる他の部分とどのように連携するかを理解することができます。すべての部分を理解すると、全体がどのように機能するかを非常に明確に把握できます。
現代の言語は、エンジンが基本的にブラックボックスである現代の自動車に似ており、平均的な自動車の所有者には理解できないほど複雑です。これらの車は、多くのことを実行できます。最近では、自力で運転することを積極的に学習しています。そして、その複雑さと快適さにより、ユーザーインターフェイスは、エンジンで実際に行われているものからはるかに離れています。
Cでプログラミングを学ぶと、コンピューターを構成する多くのネジやナットに触れることになります。これにより、マシン自体の理解を深めることができます。たとえば、次のような長い文字列を作成するのが得策ではない理由を理解できます。
java.lang.String result = "";
for(int i = 0; i < components.size; i++) {
result = result + components[i];
};
(これは正しいJavaであり、しばらく使用していません...)しかし、そうであり、連結する数百万の小さなコンポーネントがある場合に、このコードが完全に停止する理由です。経験豊富なCプログラマーは、問題がどこにあるかを即座に知っており、そもそもそのようなコードを書くことを避けるでしょう。
for(int i = 0; i < strlen(s); i++)
、Cで書くと、ループも2次の複雑さを持ち、Javaの例と同じくらい明白です;
「プログラミングの背後にある原則」、特に理論的原則を学ぶにはCよりも優れた言語がありますが、Cは工芸について実用的で重要なことを学ぶのに良いかもしれません。greyfadeの答えは確かに正しいですが、私見では、自分でメモリを管理する方法よりもCから学ぶことができます。例えば、
例外がない場合に完全なエラー処理を行うプログラムを作成する方法
オブジェクト指向の言語サポートなしでプログラムに構造を作成する方法
動的なサイズ変更可能なリスト、辞書、または有用な文字列抽象化などのデータ構造がない場合のデータの処理方法
コンパイラーまたはランタイム環境が自動的に警告しない場合でも、配列オーバーフローなどの一般的なエラーを回避する方法
テンプレートまたはジェネリックの言語サポートなしでジェネリックソリューションを作成する方法
もちろん、自分で記憶を管理する方法を学ぶことができると言いましたか?;-)
さらに、Cを学習することにより、C ++、Java、C#、Objective Cの構文上の共通点がどこから来たのかを学習します。
2005年、Joel Spolskyは、他の高レベル言語よりも先にCを学習することを推奨しています。彼の議論は
「高レベルの言語で効率的なコードを作成することはできません。」
「コンパイラやオペレーティングシステムで作業することはできません。これらは、最高のプログラミングジョブの1つです。」
「大規模なプロジェクトのアーキテクチャを作成することは決して信頼できません」
「while(*s++ = *t++);
文字列をコピーする理由を説明できない場合、またはそれが世界で最も自然なことではない場合は、迷信に基づいてプログラミングしています
もちろん、彼が書いたことは確かに議論の余地がありますが、彼の議論の多くは今日でも有効です。
コンピューティングの2つの基本的な抽象概念は、チューリングマシンとラムダ計算です。Cは、チューリングマシンの計算ビューを試すための方法です。ただし、Cには独自の計算モデルが付属していることに注意してください。したがって、Cを学習すると、実際のアーキテクチャとはまったく異なるC抽象マシンの低レベルの詳細がわかります。Cで最初に教えられたことの1つは、「賢い」トリックを適用することでコンパイラーの裏をかこうとしないことでした。他の高水準言語と同様に、Cで記述すると、コンパイラーは抽象的なCマシンで何をするかを理解し、実際のハードウェアでそれを実現します。
つまり、Cを学習することで、ハードウェアで実際に何が起こるかを必ずしも把握できるとは限りません。「Cはマシンに近い」は「ほとんどの高レベル言語よりも近い」と理解されるべきです。「どのように機能するか」のより正確な図が必要な場合、ソフトウェアアーキテクチャを直接学習することは、よりやりがいのあることです。
一方、Cを学習すると、システムプログラミング、低レベルの型、ポインター、特にメモリの割り当てに慣れることができます。学習アルゴリズムとデータ構造に関しては、他の言語ではなくCで学習する利点はありません。