Tip:
Highlight text to annotate it
X
[Powered by Google Translate] [Valgrindは]
[ネイトHardison、ハーバード大学]
これは、CS50、CS50.TVです]
Cプログラムの中で最も困難なバグの一部
メモリの不始末から来ています。
物事を台無しにする方法は膨大な数がありますが、
メモリの間違った量を割り当てるなど、
、変数を初期化するのを忘れる
バッファの終わりの前または後に書き込み、
とメモリを複数回解放しておく。
断続的なクラッシュからの症状の範囲
神秘的に上書きされた値に、
多くの場合、場所や時間に遠く元のエラーから削除されます。
基礎となる根本的な原因に戻って観測された問題のトレース
挑戦することができ、
しかし幸いなことにValgrindの呼ば役立つプログラムがあり
それは助けるために多くを行うことができます。
>> あなたは、有効にするには、Valgrindの下でプログラムを実行する
ヒープメモリの割り当てとアクセスの広範なチェック。
Valgrindは問題を検出すると、それは、あなたの即時を与える
あなたがすることができます直接情報
もっと簡単に見つけ、問題を解決します。
少なく致命的なメモリの問題に関する報告書はまた、Valgrindは
メモリリークなど、ヒープメモリを割り当て、
そしてそれを解放するのを忘れ。
私たちのデバッガで、コンパイラ、Clangの、、GDBのような
Valgrindはフリーソフトウェアであり、それは、アプライアンスにインストールされています。
Valgrindは、バイナリ実行ファイル上で実行
ないあなた。cまたはHのソースコードファイル、
ので、あなたのプログラムの最新のコピーをコンパイルしていることを確認してください
Clangのを使用したりしてください。
その後、Valgrindの下でプログラムを実行すると、することができます
ただ単語Valgrindの持つ標準のプログラムコマンドの前に付けるのと同じくらい簡単、
これは、Valgrindは起動し、その中にプログラムを実行します。
開始時に、Valgrindは、いくつかの複雑なを行います
、メモリチェック用の実行ファイルを設定するjiggering
ので、起動して実行するためにビットを取ることができます。
次に、プログラムは、はるかにゆっくりと、正常に実行され、それになります
それが終了したときに、Valgrindは、メモリ使用量の要約を印刷します。
すべてがうまくいけば、それは次のようになります。
この場合、。/ clean_program
私が実行したいプログラムへのパスです。
そして、この1つの任意の引数を取りませんが、
それがなかった場合、私はいつものようにコマンドの終わりまでにタックそれらをちょうど思います。
きれいなプログラムはちょうど私が作成した愚かな小さなプログラムです
それは、ヒープ上にint型のブロックのためのスペースを割り当て
それらの中にいくつかの値を入れて、全体のブロックを解放します。
これは、エラーや漏れがない、のために撮影しているものです。
>> もう一つの重要なメトリックは、割り当てられたバイト数の合計です。
あなたの割り当てはメガバイト以上である場合には、プログラムに応じて、
あなたはおそらく何か間違ったことをやっている。
あなたは不必要に重複して格納しますか?
あなたはそれがスタックを使用する方がよい場合には、保存のためにヒープを使用していますか?
だから、メモリエラーは本当に悪することができます。
もっとあからさまなものは、壮大なクラッシュを引き起こす
でもそれでさえ、それはまだ特定することが難しいことができます
実際に何がクラッシュを引き起こした。
もっと知らぬ間に、メモリエラーによるプログラム
それでもきれいにコンパイルすることができます
し、正しく動作するように見えることができます
あなたは、ほとんどの時間幸運を得ることに成功しているため。
いくつかの "成功した結果、"後
あなただけの、クラッシュはコンピュータのまぐれだと思うかもしれません
しかし、コンピュータは間違ったことはありません。
>> Valgrindのを実行すると、目に見えるメモリエラーの原因を追跡するのに役立ちます
同様に、あなたもまだ知りません潜むエラーを見つける。
Valgrindは問題を検出するたびに、それが観測されたかについての情報を出力します。
各項目には、かなり簡潔です -
違反命令のソース行、問題が何であるか、
関与メモリについて、少し情報 -
しばしばそれは、適切な場所にあなたの注意を向けるために十分な情報です。
ここにバグのあるプログラムで実行されているValgrindはの例です。
それはヒープメモリの無効な読み取りを行います。
我々は、コンパイルでエラーや警告が表示されない。
おっと、エラー·サマリには、2つのエラーがあることを言う -
サイズ4の2つの無効な読み取り - バイトです。
両方悪いが、invalid_read.cのメイン機能で問題が発生して読み取り、
16行目と19行目の2番目の最初の。
コードを見てみましょう。
printfの私たちのメモリブロックの末尾を超えて1 intを読み取ろうとするの最初の呼び出しのように見えます。
我々は、Valgrindの出力を振り返ってみると、
我々は、Valgrindはまさにその私たちに語ったことがわかります。
我々が読んでしようとしているアドレスは、0バイトを開始
サイズ16バイトのブロックの終わり過去 -
我々は割り当てた4つの32ビット整数。
つまり、私たちが読むことを試みていたアドレスは、右の私たちのブロックの終わりで始まり
私達は私達の悪いprintfの呼び出しで参照と同じように。
さて、無効な読み取りは、たいしたことのように見えないかもしれません
しかし、あなたのプログラムの流れを制御するために、そのデータを使用している場合 -
if文やループの一部として、例えば -
その後、物事は静かに悪く行くことができます。
私はinvalid_readプログラムを実行することができますどのように見守る
と普通の何も出も起こりません。
怖いよね?
>> 今、あなたのコード内で発生する可能性のあるエラーのいくつかのより多くの種類を見てみましょう、
そして我々はValgrindはそれらを検出する方法を見ていきます。
私達はちょうど、invalid_readの例を見ました
だから今invalid_writeをチェックアウトしてみましょう。
繰り返しになりますが、コンパイル時にエラーや警告なし。
さて、Valgrindはこのプログラムで2つのエラーがあることを言う -
とinvalid_writeとinvalid_read。
このコードをチェックアウトしてみましょう。
我々は古典的なstrlenのプラス1バグのインスタンスを持っているように見えます。
コードはmallocスペースの余分なバイトをしません
文字/ 0のため、
ので、strコピーがssubstrlenでそれを書くために行ったとき、 "CS50岩!"
それは私たちのブロックの終わりを超えて1バイトを書いた。
我々は、printfに我々の呼び出しを行ったときinvalid_readが来る。
それは/ 0の文字を読み取るときにprintfの無効なメモリを読み終わる
それがこのE列の端で見えるようになり、印刷です。
しかし、これのどれもValgrindのを免れない。
我々は、それがstrのコピーの一部としてinvalid_writeをつかまえたことがわかり
メインの11行目、およびinvalid_readはprintfの一部です。
Valgrindは、上の岩。
繰り返しますが、これは大したことのように見えないかもしれません。
我々は、Valgrindはの外で何度も何度もこのプログラムを実行することができます
とエラーの症状が表示されない。
>> しかし、参照するときに、このわずかな変化を見てみましょう
どのように物事は本当に悪い得ることができます。
だから、付与された、我々は、このコードに少しより多くのものを乱用している。
我々は唯一の2つの文字列のヒープで領域を割り当てている
CS50岩の長さは、
今回は、/ 0の文字を覚える。
しかし、その後、我々はメモリブロックに超長い文字列に投げる
Sは、を指している。
どのような効果はTポイントにそのメモリブロックにあるのだろうか?
まあ、Sにちょうど隣接のメモリへのTポイントであれば、
その直後に来て、
それから私達はTの部分の上に書かれているかもしれません
このコードを実行してみましょう。
何が起こったのかを見てみましょう。
我々は、ヒープブロックの両方に格納された文字列が正しく印字されているように見えた。
何も全然間違っていないようです。
しかし、のは私たちのコードに戻りましょうと
我々はCS50岩をコピー行をコメントアウト
第2のメモリブロックには、tが指す。
さて、私たちがすべきは、このコードを実行したとき
最初のメモリブロックの内容がプリントアウトしてください。
おっと、我々は、strコピーしなかったにもかかわらず、
第二ヒープブロックに任意の文字が、1は、Tが指す
我々は、印刷を取り出す。
確かに、文字列には、我々は最初のブロックに詰め
第一ブロックと第2ブロックにし、オーバーラン
すべてが正常に見えること。
Valgrindは、しかし、私達に本当の物語を告げる。
そうしよう。
それらのすべてが無効な読み取りと書き込みを行います。
>> エラーの別の種類の例を見てみましょう。
ここでは、むしろ不幸な何かをする。
我々は、ヒープ上のint型のためのスペースをつかむ
そのスペースを指すように - P - と我々はint型のポインタを初期化します。
しかし、我々のポインタが初期化されている間、
それが指しているデータは、単にヒープのその部分にどんなジャンクでもあります。
だから我々は、int iにそのデータをロードすると、
我々は、技術的には、iを初期化
しかし、我々はジャンクデータを使用して行います。
便利なデバッグ用マクロであると主張するための呼び出し、
適切な名前断言ライブラリで定義され、
中断は、そのテスト条件が失敗した場合にプログラムします。
iが0でない場合、つまり、。
ヒープ領域にあったものに応じて、pの指す、
このプログラムは、時には仕事と他の回で失敗する可能性があります。
それがうまくいけば、我々は幸運取得している。
コンパイラは、このエラーをキャッチしますが、必ず遺言をValgrindはないでしょう。
そこに我々は、ジャンクデータの私達の使用に起因するエラーを参照してください。
>> あなたはヒープメモリを割り当て、それを解放するか、それを解放しない場合、
それは、リークと呼ばれています。
終了を実行するとすぐに小さな、短命のプログラムの場合、
リークは、かなり無害です
が、より大きな規模のプロジェクトのためにおよび/または寿命、
小さくてもリークが主要なものに悪化することができます。
CS50の場合、我々はあなたに期待していますか
あなたが割り当てるヒープメモリのすべてを解放の世話をし、
私たちは、あなたが手動プロセスを適切に処理するためのスキルを構築したいので、
Cで必要
そのためには、あなたのプログラムは正確を持つべき
mallocとfreeの呼び出しの間に1対1に対応。
幸いなことに、Valgrindはあまりにメモリリークのお手伝いをすることができます。
ここで割り当てleak.c呼ば漏れプログラムです
ヒープ上の空間は、それに書き込み、それを解放することはありません。
我々は、Valgrindの下でそれを確認し、実行してそれをコンパイル
そして我々は、我々はメモリエラーを持たない一方で、あることがわかり
我々は一つ漏れを持っている。
間違いなく失われた16バイトがありますが、
プログラムが終了したときにそのメモリへのポインタが範囲内にありませんでしたことを意味している。
さて、Valgrindは、私たちに漏れに関する情報のトンを与えるものではありません
しかし、我々は、そのレポートの一番下に向かって与えることをこの小さなメモに従うならば
で再実行する - リークチェック=フル
メモリリークの完全な詳細を表示するには、
我々はより多くの情報を取得します。
さて、ヒープ要約すると、
失われた記憶が最初に割り当てられた場所Valgrindは教えてくれる。
私たちは、ソースコードで見てから知っているのと同様に、
Valgrindは、我々はメモリリークが発生することを通知し
leak.cの8行目に呼び出されたmallocで割り当てられ
main関数インチ
かなり気の利いた。
>> Valgrindは、これらの用語を使用してリークを分類します。
間違いなく失われた - これは、ヒープに割り当てられたメモリです
どのプログラムは、もはやポインタを持っていませんします。
Valgrindは一度ポインタを持っていたことを知っていますが、以来、それのトラックを失っている。
このメモリは間違いなくリークされます。
間接的に失われた - これは、ヒープに割り当てられたメモリです
どちらにそれへのポインタのみがも失われます。
たとえば、リンクリストの最初のノードへのポインタを失った場合には、
その後、最初のノード自体は間違いなく、失われてしまう
すべての後続ノードは、間接的に失われることになるでしょう。
おそらく失われた - これは、ヒープに割り当てられたメモリです
どちらにValgrindはポインタかどうかがあるかどうかを確認することはできません。
まだ到達は、ヒープに割り当てられたメモリです
プログラムがまだ終了時にポインタを持っていると、
これは典型的にグローバル変数が指すことを意味します。
これらのリークをチェックするには、オプションを含まなければならないでしょう
- まだ到達可能=はい
Valgrindはのあなたの呼び出しインチ
>> これらの様々な例は、それらをクリーンアップするためのさまざまな戦略を必要とするかもしれません
しかし漏れは排除されるべきです。
残念なことに、リークを修正するのは非常に難しいことができ、
自由への不正な呼び出しは、プログラムを爆破することができるからです。
たとえば、我々はinvalid_free.cを見れば、
私たちは、不良メモリ割り当て解除の例を参照してください。
全体のブロックを解放するために、単一の呼び出しがどうあるべきか
int_blockが指すメモリの、
代わりに、各intサイズのセクションを解放しようとする試みとなっている
個別のメモリ。
これは壊滅的に失敗します。
ブーム!どのようなエラー。
これは間違いなく良いではありません。
あなたは、この種のエラーで立ち往生している、しかし、あなたは、どこを見ればわからない場合
あなたの新しい親友に頼る。
あなたはそれを推測 - Valgrindは。
Valgrindは、いつものように、次第です正確に知っている。
allocと無料のカウントは一致していない。
私達は1 allocと4解放を持っている。
ここで、最初の悪い無料通話とValgrindはまた教えてくれる -
爆発を引き起こしたものは - から来ている -
16行目。
ご覧のように、自由に悪いコールは、本当に悪いです
従って私達はあなたのプログラムのリークをさせることをお勧めします
あなたは、その機能が正しい得ることに取り組んでいる最中。
あなたのプログラムが正常に動作した後にのみ漏れを探し始める、
他のエラーなし。
>> そして、それは我々がこの動画に持っているすべてです。
今、あなたは何を待っている?
今すぐあなたのプログラムにValgrindは実行行く。
私の名前はネイトHardisonです。これはCS50です。 [CS50.TV]