← メディア一覧

freeしたら即NULLが鉄則な理由

14分49秒 | アルゴリズム基礎FE

基本情報技術者試験の頻出テーマを解説した音声コンテンツです。

トランスクリプト(字幕テキスト)

はい、今回はですね、プログラミング、特にC言語を扱う上で、まあ、避けては通れない、メモリ管理のある、ちょっと恐ろしい落とし穴について皆さんと一緒に深く見ていきたいと思います。 ええ。手元にあるのが、ポインタの階段っていう、新人エンジニアとベテランエンジニアの対話形式で書かれた短い物語なんです。 これは単なる技術的なミスっていう話じゃなくて、システム全体を危険にさらし続けかねない、なんていうか、時限爆弾の話なんですよね。 時限爆弾ですか。ええ。この物語だと、新人エンジニアのアキラ君が、まあ、善意からなんですけど、知らず知らずのうちにその爆弾を抱えちゃうところから始まります。 まさに。この物語を通じて、なぜ、使い終わったメモリのアドレスを持ち続けることが現場で最も恐れられるのか、その核心に迫っていきましょう。 あの基本情報技術者試験なんかで学ぶ知識が、いかに現実でクリティカルなのかがよくわかる内容になってます。それでは早速、物語の最初の場面から見ていきましょうか。 はい。物語はですね、新人エンジニアのアキラが、大きなデータを処理し終えて、こう満足感に浸ってるところから始まるんです。 うん、うん。で、彼はサーバーのリソースを節約するために、ちゃんと習った通りにフリー関数を使って、使い終わったメモリを解放するんですね。 ここまではもう100点満点の行動です。借りたものはきちんと返すっていうのはプログラミングの基本的なマナーですからね。そうなんです。 だからこそアキラも自信満々でこう宣言します。「完璧なメモリ管理です。これでサーバーのメモリ不足も怖くありません。」 まあ一見、何の問題もないように聞こえますよね。ええ。ところが、ベテランのヨミは、アキラの手元に何か不穏なものが残ってることに気づくんです。 はい。それが、解放したはずのデータのアドレスがそのまま入ったポインタ変数、つまりメモ書きです。ヨミは核心を突くんですよね。 「君の手元にあるそのメモ書きは、どうするつもりだい?」と。ええ。アキラは悪びれる様子もなく答えるんです。 「さっき解放したデータの住所を控えたポインタです。一応後で参照するかもしれないと思って」って。 ああ、その「一応後で参照するかも」っていう軽い気持ちがまさに階段の始まりなんですよ。開発現場でよく聞く言葉ですよね、これ。 ええ。この時点でヨミは心の中で呟きます。「それが階段の始まりだとも知らずに」と。怖いですね。 ただ、重要なのは、この時点ではまだ何も悪いことは起きていないっていうことです。あ、そうか、まだ何も。はい、ただ非常に危険な状態にある。 例えるなら火薬庫の中で火のついてないマッチを持ってるようなものでしょうか。なるほど。それ自体は無害だけど、一歩間違えればと。 そうです。そしてアキラはその禁断の一歩を踏み出してしまう。何が怖いんですか?住所を覚えてるだけですよって言って、 その古いアドレスが指し示す先を、本当に軽い気持ちで覗いちゃうんですよね。彼の頭の中では、さっきまで自分が使っていたデータがそこにあるはずだって思い込みがあったんでしょうね。 はい。フリーっていうのはあくまでOSに対して「この土地はもう使いません」って申告するだけで、土地を更地にする作業がすぐに行われるとは限らない。 だから直後なら前の住人の痕跡が残ってることもあるんです。まさに。 で、アキラがキーを叩くと、最初はさっきまで扱っていたデータの一部が見える。うん、うん。でも、次の瞬間、画面には文字化けした奇妙な文字列と、 全く関係のない隣のプロジェクトの秘密データらしき顧客IDが表示されるんです。フラッシュバックのように。ええ、アキラはもちろんパニックになります。 「なん、何ですかこれ?僕がさっき扱っていたデータじゃない」って。そこで、ベテランのヨミがこの問題の本質を完璧に捉えた非常に秀逸な例え話をするわけです。 はい。彼が口にしたのが「解体されたアパートの部屋番号」という言葉ですね。この比喩は本当に分かりやすいですよね。 ええ。ここで少しこの例えを詳しく紐解いていきましょうか。まず、プログラムがマロック関数なんかでメモリを確保するっていうのは、 この例えで言うと、アパートの部屋を借りることですね。そうですね。そして、その借りた部屋の住所、例えば302号室って書かれたメモ。 これがポインタ変数。その通りです。プログラムはこのメモを頼りに自分のデータがどこにあるのかを把握してるわけです。 で、アキラが行ったフリー関数によるメモリ解放、これがつまり部屋の契約が終わり、アパートが解体されることを意味します。 大家さんであるOSは「この土地は空きましたよ」と認識して、すぐに別の人、つまり別のプログラムにその土地を貸し出す準備を始める。 問題はここからなんですね。ええ。アキラは部屋を解約してアパートも解体されたのに、手元には302号室と書かれた古い住所メモを持ち続けていた。 これが解放後もポインタを持ち続けるという危険な状態の正体と。はい。そして、彼が最後にしてしまった致命的な行為。 古いポインタへのアクセスは、新しい住人がもう家を建ててるかもしれない土地に、古い住所のメモだけを頼りに勝手に立ち入る行為。 まさに不法侵入ですね。だからそこに行ってみるまで何があるか分からない。そうなんです。アキラが見たように、新しい住人のプライベートな情報が転がってるかもしれない。 ええ。ここでこの現象を指す専門用語を導入しておきましょうか。はい。この解放済みで無効なメモリ領域を指し示し続けているポインタのことを、 ダングリングポインタ、ダングリングポインタと呼びます。ダングリング、つまり、ぶら下がっているとか、宙ぶらりんって意味ですよね。 ええ。行き先を失って不安定に浮いてるようなイメージです。なるほど。本来ポインタっていうのは、有効なメモリ領域と固く結びついているべき存在なんです。 でも差し示す先のメモリが解放されると、その結びつきが断ち切られて行き場をなくしてしまう。その宙ぶらりんな状態が深刻な問題を引き起こすと。 まさにその通りです。うーん、その宙ぶらりんな状態、想像するだけで不安定で気持ち悪いですね。アキラのPCでは顧客IDが見えちゃいましたけど、 最悪の場合、具体的にどんな大惨事に繋がる可能性があるんでしょう。大きく分けて3つの段階的に深刻化する危険性がありますね。 一つ目はアキラがまさに体験した、意図しないデータ漏洩です。データ漏洩、それって例えばECサイトなら他人の購入履歴とか、クレジットカード情報が見えちゃったり。 その通りです。金融システムなら他の人の口座情報が漏れたりする可能性もゼロではない。うわあ。解放された領域に別のプログラムが機密情報を書き込んでいた場合、 それを偶然読み取ってしまう可能性がある。これはもうバグというレベルじゃなく、セキュリティ上の重大な脆弱性になります。それは恐ろしいですね。 では2つ目の危険性というのは。2つ目はさらに悪質で、データ破壊です。データ破壊。 もしダングリングポインタを使ってそのメモリ領域に何かを書き込んでしまった場合を想像してみてください。ああ。 さっきのアパートの例えで言うと、不法侵入した先の家で他人の家具の配置を勝手に変えたり、壁にガキガキしたりするようなものですかね。まさにそれです。 今まさにその領域を正しく使っている他のプログラムのデータを意図せず上書きして破壊する行為に他ならないんです。うーん。 そうなると、関連するプログラムが予期せぬ動作をしたり、データベースの重要なデータが修復不可能なレベルで破損したりする原因になります。 自分のプログラムだけでなくシステム全体に害を及ぼすことになる。聞いてるだけで冷や汗が出ますね。そして最悪のシナリオである3つ目は。 プログラムの即時クラッシュ、あるいはシステム全体の不完全化ですね。システム全体が道連れに。ええ。 OSが管理している重要な領域を偶然書き換えてしまったり、不正なメモリアクセスとして検知されたりすると、プログラムはOSによって即座に強制終了させられます。 はい。これがシステムの根幹部分で起これば、OSごと停止してしまう。いわゆるブルースクリーンとか、カーネルパニックといった事態に発展しかねません。 実は経験があったり。あー、お恥ずかしながら若い頃に書いたコードでまさにこれをやってしまったことがあって。ほう。 テスト中は全く問題が出なかったのに、納品後にお客様から「週に1回くらい理由なくアプリが落ちる」って報告が来たんです。 原因が全く分からず数週間悩んだ末に、ある非常にまれなエラー処理のルートを通った時だけ、解放済みのポインタにアクセスしてるのを見つけて、 あの時の冷や汗は忘れられませんね。再現性の低いバグほど怖いものはないです。実体験が伴うと説得力が違いますね。 では、そんな恐ろしい呪いを避けるために、一体どうすればいいんでしょうか。物語の結論として、ヨミがアキラに「現場で生き残るための鉄則」を教えます。 はい。その鉄則は驚くほどシンプルです。「フリーしたら即ナル」。もうこれに尽きます。なるほど。フリーしたら即ナル。 シンプルですけど、そもそもなぜフリー関数自体がそのポインタをナルにしてくれないんでしょう。あー、非常に良い質問ですね。 一手間増えるわけで、うっかり忘れそうですし。なんだか言語の設計ミスみたいに感じてしまうんですけど。そこがC言語という言語の、なんていうか、哲学に関わる部分なんです。 フリーという関数はあくまでOSに「このメモリ領域はもう使いません」と通知するだけで、プログラマーの手元にあるポインタ変数の中身を自動的に書き換えることはしない。 なぜですか?なぜなら、C言語は、プログラマーが何をしているか完全に理解している、という前提で最大限のパフォーマンスと制御を優先するからなんです。 ポインタの値を勝手に変えないことで、例えば解放した直後に別の処理を挟むといった高度な使い方も可能になる。良くも悪くも、プログラマーに全責任を委ねてるんですよ。 なるほど。自由度が高い分、責任も重い。と、ええ。そこで、ナルを代入するという行為がその責任を果たすための重要な儀式になります。儀式。 はい。ナルというのはプログラミングの世界で「何もない、どこも指し示していない」ことを示す特別な値です。 ポインタ変数にナルを代入するっていうことは、住所メモに書かれていた302号室という住所を物理的に消しゴムで消して白紙にするようなものなんです。 住所の情報そのものを能動的に消し去ってしまうわけですね。その通りです。これにより、たとえプログラムの他の部分で間違ってそのポインタを使おうとしても、どこにもアクセスしようがない。 あ、なるほど。多くのシステムでは、ナルポインタへのアクセスは安全に検知されて、「不正なアクセスです」とプログラムがその場で即座に停止するようになってます。 データ破壊のような、より深刻で気づきにくい事態に発展する前に問題を未然に防げるんです。未練を断ち切るという物語の表現がまさに的確ですね。 ええ。ちなみに最近の言語、例えばPythonとかJavaではこのあたりはどうなってるんでしょう。ああ、いい点ですね。 PythonやJavaのようなモダンな言語では、この種のメモリ管理はガベージコレクションという仕組みでほとんど自動化されています。 なのでプログラマーが明示的にメモリを解放する必要がない。ということは、ダングリングポインタみたいな問題は。原理的に起こりにくくなっています。 ではなぜ今でもC言語が使われ続けるんでしょう。やはりC言語のようなハードウェアに近いレベルでの細かな制御と、それによる圧倒的なパフォーマンスが求められる領域があるからですね。 OSのカーネルとか、自動車の制御システム、IoTデバイスみたいな組み込みシステムでは、メモリの1バイトまで厳密に管理したい。そのためにあえて手動管理という選択肢が残されているんです。 なるほど。この物語はその強力な力の代償としての大きな責任を教えてくれるわけですね。ええ。この話を聞くとソースの最後の言葉が深く響きます。 「試験でポインタとメモリの有効範囲がしつこく問われるのは、君を不法侵入者にしないための優しさなんだよ」と。全くです。 単なる暗記項目ではなく、エンジニアとしての倫理観や安全に関わる本当に重要な知識だということがよくわかります。 自分のプログラムだけでなく、システム全体、そして他のユーザーのデータを守るための基本的な作法と言えるでしょう。さて、今日の物語から見えてきた重要なポイントを整理しましょう。 ソースの最後にあった必勝10秒チェックが今回の学びを完璧に要約してくれてますね。はい。まず1つ目、試験の罠。メモリを解放してもポインタ変数の中身、住所は自動的には消えない。 フリーはあくまでOSへの返却宣言。手元のメモを消してくれるおせっかいな機能はないという点ですね。ええ。そして2つ目、現場の恐怖。 解放済みの住所にアクセスすると他人のデータを破壊するかシステムがクラッシュする。解体されたアパートへの不法侵入がもたらす最悪の結果。 データ漏洩、データ破壊、システムダウンの3段活用です。そして最も重要な3つ目の対策。鉄則、フリーしたら即ナル。住所を物理的に消去して未練を断ち切れ。 これがダングリングポインタという呪いから身を守る唯一にして最も確実な方法。そういうことです。 いやあ、短い物語でしたけど、C言語におけるメモリ管理の核心とその怖さ、そしてプロとしての責任が凝縮されていましたね。 そうですね。それで最後に少し考えてみてほしいことがあるんです。と言いますと。と。ダングリングポインタの問題ってプログラミングの世界だけの話ではないかもしれないなと。 ほう。私たちの生活の中でも、過去の関係とか古い情報とか、もはや有効でなくなったものへの接続、つまりポインタを持ち続けてしまうことありませんか? ああ、なるほど。その接続がもはや危険だと知りながらついアクセスしようとすると、予期せぬ問題を引き起こしたり自分や他人を傷つけたりする。 どの繋がりを意識的に断ち切り、何をナル、つまり空っぽにすべきかを見極めること。はい。 それは安全なコードを書く上でも、あるいは私たちの人生における意思決定においても非常に重要なスキルと言えるんじゃないでしょうか。

このコンテンツは Web society で視聴・学習できます。