14

これこれのような質問の存在を知っています。説明させてください。

Joelの記事BacktoBasicsを読み SOに関する多くの同様の質問を見て、Cのようなことを知っているとあなたがより優れた高レベルのプログラマーになる状況の具体例は何であるか疑問に思い始めました。

私が知りたいのは、この例がたくさんあるかどうかです。多くの場合、この質問に対する答えは、「Cを知っていると、内部で何が起こっているかをよりよく理解できる」または「プログラムの強固な基盤が必要」のようなものであり、これらの答えはあまり意味がありません。低レベルの概念を知ることであなたが恩恵を受けるさまざまな具体的な方法を理解したいと思います。

Joelは、いくつかの例を示しました。バイナリデータベースとXML、および文字列です。しかし、2つの例は、Cやアセンブリの学習を実際に正当化するものではありません。だから私の質問はこれです:あなたをより良いハイレベルのプログラマーにするCを知っていることの具体的な例は何ですか?

4

10 に答える 10

15

学生に教えたり、高級言語しか勉強していない人と一緒に仕事をしたりした経験では、彼らはある程度の抽象化で考える傾向があり、「すべてが無料でやってくる」と思っています。彼らは非常に有能なプログラマーになることができますが、最終的にはパフォーマンスの問題があるコードを処理する必要があり、それから彼らを噛むことになります。

Cを頻繁に使用する場合は、メモリの割り当てについて考えます。多くの場合、メモリレイアウト(およびそれが問題になる場合はキャッシュの局所性)について考えます。あなたは、特定のグラフィックス操作がどのようにそしてなぜ多くの費用がかかるのかを理解しています。特定のソケットの動作がどれほど効率的または非効率的か。バッファのしくみなど。抽象化をカバーの下でどのように実装するかを知っているときに高級言語で抽象化を使用すると、パフォーマンスについて考えるときに「その余分な秘密のソース」が得られることがあると思います。

たとえば、Javaにはガベージコレクタがあり、メモリに直接割り当てることはできません。それでも、これがCで問題になるのと同じ理由で、パフォーマンスに影響を与える特定の設計選択(たとえば、カスタムデータ構造を使用)を行うことができます。

また、より一般的には、パワープログラマーがbig-O表記(ほとんどの学校が教えている)を知っているだけでなく、実際のアプリケーションでは定数も重要である(学校が無視しようとしている)ことが重要だと感じています。 。私の逸話的な経験は、おそらく私が上で説明したことのために、両方の言語レベルのスキルを持つ人々は定数をよりよく理解する傾向があるということです。

さらに、私が見た多くの高レベルのシステムは、低レベルのライブラリおよびインフラストラクチャとインターフェースします。たとえば、一部の通信、データベース、またはグラフィックライブラリ。特定のデバイス用の一部のドライバーなど。あなたがパワープログラマーである場合、最終的にはそこに挑戦しなければならない可能性があり、少なくとも何が起こっているのかを理解するのに役立ちます。

于 2010-01-16T15:58:28.003 に答える
9

低レベルのものを知ることは大いに役立ちます。

レーシングドライバーになるには、タイヤが道路をグリップする方法の基本的な物理学を学び、理解する必要があります。誰でもかなり速く運転することを学ぶことができますが、パフォーマンスの最後の数パーセントを得るには、「低レベル」のもの(力と摩擦、レーシングライン、細かいスロットルとブレーキ制御など)をよく理解する必要があります。レースに勝つ。

たとえば、コンピュータでCPUアーキテクチャがどのように機能するかを理解している場合は、CPUアーキテクチャでより適切に機能するコードを記述できます(たとえば、各CPUキャッシュラインに特定のCPUキャッシュサイズまたは特定のバイト数があることがわかっている場合は、次のことができます。データ構造とそれらにアクセスする方法を調整して、キャッシュを最大限に活用します。たとえば、CPUキャッシュがあるため、配列の多くの要素を順番に処理する方が、ランダムな要素を処理するよりも高速です。マルチコアコンピューターを使用している場合は、スレッド化作業などの低レベルの手法を理解することで大きなメリットが得られます(低レベルを理解しないと、スレッド化で災害が発生する可能性があります)。

ディスクI/Oとキャッシュがどのように機能するかを理解している場合は、ファイル操作を変更して適切に機能させることができます(たとえば、あるファイルから読み取り、別のファイルに書き込む場合、RAM内の大量のデータを処理すると、I/Oの競合を減らすことができます。コードの読み取りフェーズと書き込みフェーズの間で、スループットを大幅に向上させます)

仮想関数がどのように機能するかを理解していれば、仮想関数をうまく使用する高レベルのコードを設計できます。誤って使用すると、パフォーマンスが大幅に低下する可能性があります。

描画の処理方法を理解していれば、巧妙なトリックを使用して描画速度を向上させることができます。たとえば、64個の白と黒の正方形を交互に描くことでチェス盤を描くことができます。ただし、多くの場合、32個の白い正方形を描画してから32個の黒い正方形を描画する方が高速です(描画色を64回ではなく2回変更するだけでよいため)。ただし、実際にはボード全体を黒で描画してから、ボード全体に4つのストライプをXORし、ボードの下に4つのストライプを白で描画できます。これにより、さらに高速になります(2つの色が変更され、64ではなく9つの長方形のみが描画されます)。このチェス盤のトリックは、非常に重要なプログラミングスキルである水平思考を教えてくれます。アルゴリズムを適切に設計することにより、プログラムの動作に大きな違いをもたらすことがよくあります。

于 2010-01-16T16:09:47.027 に答える
5

C、またはさらに言えば、低水準プログラミング言語を理解することで、メモリ使用量(つまり、数百万の重いオブジェクトを作成するのはなぜ悪いことなのか)、ポインター/オブジェクト参照のしくみなどを理解する機会が得られます。

問題は、ますます多くのレベルの抽象化を作成しているため、レゴが実際にどのように機能するかを理解せずに、多くの「レゴブロック」プログラミングを行っていることに気付くということです。そして、ほぼ無限の資源を持つことで、私たちは記憶や水のような資源を扱い始め、その状況でより多くの鉄を投げることによって問題を解決する傾向があります。

Cに限定されませんが、Arduinoや旧式の8ビットプロセッサなど、メモリに制約のある非常に小さなシステムで低レベルで作業することには大きなメリットがあります。これにより、はるかに親しみやすいパッケージで金属コーディングに近い体験ができます。アプリを512Kに圧縮するのに時間を費やした後、日常のプログラミングでこれらのスキルをより大きなレベルで適用できるようになります。

したがって、言語自体は重要ではありませんが、すべてのビットがどのように組み合わされ、ハードウェアに近いレベルで効果的に機能するかを深く理解することは、ソフトウェア開発者にとって有益な一連のスキルです。

于 2010-01-16T16:02:39.783 に答える
2

1つは、Cを知っていると、OSやその他の高級言語でメモリがどのように機能するかを理解するのに役立ちます。C#またはJavaプログラムがメモリ使用量に膨れ上がるとき、参照(基本的には単なるポインタ)もメモリを消費することを理解し、実装されているデータ構造(Cで独自に作成することで得られる)の数を理解することは、それを理解するのに役立ちますディクショナリは、実際には使用されていない大量のメモリを予約しています。

もう1つは、Cを知っていると、低レベルのオペレーティングシステム機能を利用する方法を理解するのに役立ちます。これは頻繁には必要ありませんが、メモリマップトファイルが必要な場合や、C#でマーシャリングを使用する場合があります。Cは、それが発生したときに何をしているかを理解するのに大いに役立ちます。

Cもネットワークプロトコルの理解に役立ったと思いますが、具体的な例に指を置くことはできません。先日、Cのビットフィールドが「基本的に役に立たない」と誰かが不満を言っている別のSOの質問を読んでいて、Cビットフィールドが低レベルのネットワークプロトコルをどれほどエレガントに表現しているかを考えていました。ビットの構造を扱う高級言語は常に混乱してしまいます!

于 2010-01-16T16:01:03.723 に答える
2

一般的に、あなたが知っているほど、あなたはより良いプログラマーになるでしょう。

ただし、Cなどの別の言語を知っていると、間違ったことをする可能性があります。これは、高級言語(Python、PHPなど)では当てはまらない仮定があるためです。たとえば、リストの長さを見つけることがO(N)であると想定する場合があります。ここで、Nはリストの長さです。ただし、これは多くの高級言語インスタンスにはおそらく当てはまりません。Pythonでは、ほとんどのリストのようなものの場合、コストはO(1)です。

言語の詳細についてもっと知ることは助けになりますが、より一般的に知ることは間違った仮定をすることにつながるかもしれません。

于 2010-01-16T16:05:50.453 に答える
1

Cを「知っている」だけでは、あなたは良くなりません。

ただし、ネイティブバイナリがどのように機能するか、CPUがどのように機能するか、アーキテクチャの制限は何かをすべて理解していれば、CPUにとってより簡単なコードを記述できます。

たとえば、L1 / L2キャッシュが作業にどのように影響するか、L1/L2キャッシュでより多くのヒットを得るようにコードをどのように作成する必要があるかなどです。C / C ++を使用して大規模な最適化を行う場合は、そのようなことを行う必要があります。

于 2010-01-16T15:51:57.487 に答える
1

Cは他の多くの言語よりもベアメタルに近いので、Cについてはあまり知りません。自分で行う必要があるため、メモリの割り当て/割り当て解除の方法をよりよく理解する必要があります。自分でそれを行うことは、あなたが下す多くの決定の意味を理解するのに役立ちます。

私にとって、コンパイラー/インタープリターが(基本的に)コードをマシンにマップする方法を理解している限り、どの言語でも受け入れられます。これを直接公開する言語で行うのは少し簡単ですが、少し読んで、メモリがどのように割り当てられ、編成されているか、どの種類のインデックスパターンが他のパターンよりも最適であるか、どのような構成であるかを理解できるはずです。特定のアプリケーションなどでより効率的です。

さらに重要なのは、オペレーティングシステム、メモリアーキテクチャ、およびアルゴリズムをよく理解していることだと思います。アルゴリズムがどのように機能するか、あるアルゴリズムまたはデータ構造を別のアルゴリズムまたはデータ構造よりも選択する方がよい理由(HashSetとListなど)、およびコードがマシンにどのようにマッピングされるかを理解している場合は、使用している言語は関係ありません。 。

于 2010-01-16T16:01:58.260 に答える
1

これは、プログラミング、具体的にはCを理解する方法を学び、学んだ私の経験です。これは1990年代初頭にさかのぼるので、少し古風かもしれませんが、情熱と意欲は重要です。

  • EGA / VGAプログラミングなど、コンピューターの低レベルの原則を理解する方法を学びます。ここに、PCのCプログラマーガイドにあるSimtelアーカイブへのリンクがあります。
  • TSRの仕組みを理解する
  • ボブ・スタウトのスニペットのアーカイブ全体をダウンロードしてください。これは、1つのことだけを行うCコードの大きなコレクションです。スニペットのコレクションは、移植性を追求しているだけでなく、それらを調べて理解してください。
  • オンラインで国際難読化Cコードコンテスト(IOCCC)を閲覧し、Cコードがどのように悪用されるかを確認し、言語の内部を理解します。最悪のコード乱用が勝者です!アーカイブをダウンロードして調べてください。
  • 私と同じように、私は悪名高いPonzoのCチュートリアルが大好きでした。これは、残念ながら、アーカイブを見つけるのが非常に困難でした。どこで入手できるか知っている人がいたら、コメントを残してください。リンクを含めるようにこの回答を修正します。私が覚えているもう1つのものがあります-コロナドの[ジェネリック?] Cチュートリアル、繰り返しますが、これに関する私の記憶はぼんやりしています...
  • ドブ博士の日記とCユーザージャーナルをここで見てください-まだ印刷できるかどうかはわかりませんが、それらは古典的で、印刷されたコピーを手に持って家を引き裂いて入力する感覚を覚えています。何が起こるかを確認するためのコード!
  • borland.comから入手できると私が信じているTurboCv2の古代のコピーを入手し、16ビットCプログラミングで遊んで、ポインターを感じて混乱させてください...それが古くて古いことを確認してください。大丈夫。
  • ポインターを理解して学び、レガシーSimtel.netにリンクします-より良い言葉を求めてCの達人を達成するための重要なリンクです。また、Cプログラミング言語に関連するダウンロードのホストがあります-私は実際に注文したことを覚えていますSimtelCDアーカイブとCのものを探しています...
于 2010-01-16T17:02:50.713 に答える
0

他の言語があなたから抽象化するCで直接処理しなければならないいくつかのことには、明示的なメモリ管理(malloc)とポインタを直接処理することが含まれます。

私のガールフレンドは、MIT(主にJava、Scheme、Pythonを使用)をコンピューターサイエンスの学位で卒業してから1学期で、現在、コードベースがC++の会社で働いています。最初の数日間、彼女はすべてのポインター/参照などを理解するのに苦労しました。

一方、C ++からJavaへの移行は非常に簡単でした。これは、値による参照の受け渡しと参照による受け渡しについて混乱することがなかったためです。

同様に、C / C ++では、すべてが独自のプロパティを持つオブジェクトであるPythonやRubyのような言語とは対照的に、プリミティブが同じビットのセットを異なる方法で処理するコンパイラであることがはるかに明白です。

于 2010-01-16T15:55:06.137 に答える
-2

上記のアドバイスのいくつかを説明するための単純な(完全に現実的ではない)例。一見無害に見えると考えてください

while(true)
   for(Iterator iter = foo.iterator(); iter.hasNext();)
       bar.doSomething( iter.next() )

またはさらに高いレベル

while(true)
    for(Baz b: foo)
        bar.doSomething(b)

ここで考えられる問題は、 whileループをラウンドするたびに、新しいオブジェクト(イテレーター)が作成されることです。プログラマーの利便性だけが気になる場合は、後者の方が間違いなく優れています。ただし、ループを効率的にする必要がある場合、またはマシンのリソースに制約がある場合は、高級言語の設計者にほとんど翻弄されます。

たとえば、高性能Javaを実行する場合の一般的な不満は、ガベージ(割り当てられたすべてのIteratorオブジェクトなど)が再利用されている間に実行が停止することです。ソフトウェアが入ってくるミサイルの追跡、ジェット旅客機の自動操縦、またはGUIが応答を停止した理由をユーザーに不思議に思わせないようにする場合はあまり良くありません。

考えられる解決策の1つ(まだ高級言語で)は、イテレータの利便性を次のようなものに弱めることです。

Iterator iter = new Iterator();
while(true)
    for(foo.initAlreadyAllocatedIterator(iter); iter.hasNext();)
       bar.doSomething(iter.next())

しかし、これは、メモリ割り当てについて何らかのアイデアがある場合にのみ意味があります...そうでない場合は、厄介なAPIのように見えます。利便性は常にどこかでコストがかかります。低レベルのものを知っていると、それらのコストを特定して軽減するのに役立ちます。

于 2010-01-16T16:29:36.413 に答える