C言語

1.C言語は時代遅れの言語か?

 

●なぜいまだにC言語がつかわれるのか

C言語が世に出たのは、1972年で、現在使われている言語のなかでは、かなり古い言語に分類されます。また、パソコンやWeb、スマートフォンなどといった分野でも、JavaやPHP、C#などといった新しい言語が主流になり、ともすれば、C言語は必要ではない古い言語ではないか、と思われがちです。

ただ、C言語というのは不思議な言語で、C言語が世に出た当時も、そして、その後今に至るまで、数多くの「C言語よりも優れている」と思われる言語が出現してきたものの、結局生き残っているのです。

つまり、C言語と同時期、もしくはそのあとに出てきて、一時的にC言語よりも広く使われていても、その後廃れてしまったり、ほとんど使われなくなった言語は非常にたくさんあるのです。ではいったい、どうしてそんなにC言語は長い間使われ続けているのでしょうか?

 

 

●C言語の歴史

まずは、そもそもC言語とはいつ、どのようにして開発されたかということについて説明しましょう。前述のように、C言語は1972年に開発されました。開発者である、Dennis M. Ritchieは、当時Ken Thompsonらと共同で、ミニコンピュータのオペレーティングシステムであるUNIXの開発に携わっていました。このOSは、初期の段階ではアセンブリ言語を用いて開発されましたが、その後、C言語で書き直されました。

つまり、UNIXを移植するために開発されたのがC言語なわけです。そのためC言語はUNIXの副産物であると言えるでしょう。そういったこともあり、UNIX上で動作する多くのアプリケーションも、C言語で開発されることになりました。またその勢いはとどまるところを知らず、次第に大型コンピュータやパーソナルコンピュータの世界にも普及していったのです。

 

 

●C言語はこまわりがきく

この当時、C言語以外にも様々なプログラミング言語が存在していました。ではなぜ、それにもかかわらずC言語がこのように爆発的に普及したのでしょう?理由の一つは、C言語の「コンパクトさ」にあるでしょう。JavaやC#などといった言語は、C言語の欠点を補う様々な改良がなされていますが、実行に独自のフレームワークを必要としたり、同じような処理を行うにしても、C言語にはスピードの速さや、コードのコンパクトさの面でかなわない、という特徴があります。もう少し詳しく言うと、C言語によって生成されるアセンブラのコードのサイズが小さいため、スピードが速いうえに、メモリの使用も最低限で済ませることができるからで、これに関してC言語に取って代われる言語はほかにないでしょう。

そういったことから、OSなどの心臓部分を記述するのは、相変わらずC言語です。また、組み込みプログラミングと言って、家電製品や自動車の制御装置など用いられているコンピュータは、相変わらずC言語が用いられています。

小回りが利いて、スピーディーであり、それでいてアセンブラほど難しくないというのがC言語の特徴であり、その利点を生かせる分野では、C言語はまだまだ現役バリバリで活躍中です。

 

 

●OSも、プログラミング言語も最後はC言語が必要

そういったことから、Ruby、Java、PHPなどといった言語のようなC言語よりもかなり後にできた言語のコンパイラーやインタープリタも、実はC/C++言語で記述されています。同様に、サーバー系のOSとして主流である、Linuxも、「カーネル」と呼ばれる核になる部分はC言語でなくては記述できません。その他、WindowsやMacOSなどといった、主要なOSも、期間部分はC言語で記述されています。

つまり、こういったことからも、C言語がまだまだ必要であり、かつ現役のプログラミング言語であることがよくわかると思います。

 

●過去の蓄積も豊富

またもう一つの理由は、「過去の遺産の多さ」です。C言語は、後継言語であるC++言語によって、オブジェクト指向という新しい考え方のプログラミングでも用いることができるようになりました。

C++言語の最大の特徴は、C言語のソースコードをそのまま流用できることにあり、過去の遺産を継承しつつ、オブジェクト指向という新しい技術に対応できるようになっているのです。そういったこともあり、C言語は、長い間使いづけられ、その結果、ソースコードやノウハウが蓄積し、よりプログラマーにとって利用しやすい言語となって生き残ってれこれたのです。

 

 

 

2.なぜ、C言語っていうの?

 

●C言語の名前の由来

C言語という名前を聞いて、「なぜそんな名前なの」と、疑問を持った人も少なくないでしょう。実は、この「C」という言葉が言語に採用された理由は、「その前にB言語という言語があったから」ということが大きな理由です。

B言語とは、1970年にケン・トンプソンという人が、PDP-7というコンピュータ上で最初のUNIXシステム用に開発したプログラミング言語です。となると、「もしかしたら、B言語はA言語という言語をもとにして作られたのか?」と思われたかもしれませんが、B言語は、もともとBCPL(Basic Combined Programing Language)という言語を元に開発されたものなので、この推理は間違いです。

ただ、C言語は、明らかにB言語の後継の言語として作られたもので、その点に関しては、C言語の作者自身が明らかにしているので、間違いないでしょう。

 

 

●B言語との違い

B言語には、実行環境に依存しないという特徴があり、その点はC言語で受け継がれています。ただ、B言語は、もともとC言語と違い、「型の指定」がありませんでした。これは、現代の様々な言語の先取りをしているといえますが、C言語はあえてこの考え方を捨てました。

これにより、B言語のプログラマーが意識しなくてもよかったデータの「型」というものをプログラマーが自分で意識しながらプログラムをする必要が出てきました。これは一見、言語としてはB言語よりある意味では後退している、と言えなくもありません。しかし、結果的にこれがC言語を普及させるうえでプラスになった、と言っても過言ではありません。

なぜなら、れこによりプログラマーは、よりアセンブラに近いコードが記述できるようになり、コンパクトで効率的なプログラムを記述できるようになったのです。当時のコンピュータの性能は現在のものとは比較にならないほど低かったため、こういった現実的な対応が、結果としてはC言語の普及に拍車をかけたといえるでしょう。

 

 

●かつて全盛を極めたC言語

C言語が誕生したときには、すでに沢山のプログラミング言語が存在し、C言語よりもはるかに普及していたものもありました。しかし、C言語はそれらを押しのけて、やがてプログラミング言語の中でもっとく普及する、いわば「プログラミング言語の王者」となります。

しかし、この時、言語としてはC言語よりも完成度が高い言語はほかにも生地にも存在していたにもかかわらず、です。では一体、なぜそのようなことになったのでしょうか?

理由はいろいろありますが、その一つはC言語がUNIXとセットになっていた点にあるでしょう。その後、UNIXはコンピュータのOSの主流として大いに普及していき、それとともに、C言語もそれと伴い、広く使われるようになりました。

また、UNIXをまねて作られたマイクロソフト社のMS-DOSの普及もまた、それに拍車をかけました。MS-DOSは、当時主流だったパーソナルコンピュータ、IBMのIBM-PCの主要OSとして普及し、そのプログラミング言語としてもっともよくつかわれていたのが、C言語でした。この、マイクロソフトのMS-DOS(または、PC-DOS)と、C言語の組み合わせは世界中に普及し、ほぼ「デファクトスタンダード」と言ってもいいような組み合わせとして存在していました。

当時のパーソナルコンピュータは、現在のコンピュータほど性能が良くなかったために、高級言語で高パフォーマンスの言語を作れるC言語は非常に重宝されていたのです。

 

 

●C言語の後継言語

現在、C言語は、いわば「歴史的役割」を終えて、主要なプログラミング言語の第一線を退いていると言えます。しかし、その後継の言語はまだまだ現役で使用されており、そういった意味でC言語の存在感は失われていないと言えるでしょう。

C言語の直接の後継言語は、C言語にオブジェクト指向の考え方を導入したC++言語です。また、同じような意味でアップル社で開発されたObjective-Cもまた、オブジェクト指向のC言語の直接の後継言語といえるでしょう。

さらに、直接の関係はありませんが、Java言語も、文法の仕様などでC言語の営業をかなり受けており、C言語を覚えたプログラマーにとっては非常にとっつきやすい言語使用になっています。

また、マイクロソフト社が開発したC#言語も、C/C++言語の後継言語として開発され、現在では主流のプログラミング言語として君臨しています。このように、現在使用されている主要なプログラミング言語はほぼすべて、C言語の後継か、強く影響を受けた言語だということがよくわかります。

 

 

 

3.バッファーオーバーフロー

 

●バッファーオーバーフローとは何か?

アプリケーションソフトにセキュリティホールが見つかって、「悪意のあるコードが実行される可能性がある」というような内容のニュースを聞いたことがある人は少なくないでしょう。そもそも、この「悪意のあるコード」とは一体何なのでしょうか?

コンピュータウイルスやワーム、バックドア、キーロガーなどが代表的で、情報システムや提供するサービスの妨害など、悪影響を及ぼすコードが含まれるプログラムのことで、文字通りシステムに「害悪」をもたらすことを目的とした者によって作成されたプログラムのことです。

そしてそういった悪意のあるコードが、システムを攻撃する手段としてよく用いられるのが、この「バッファーオーバーフロー」という手法なのです。

 

 

●バッファーとその仕組み

バッファーオーバーフローについて説明する前に、そもそも「バッファー」とは何かということから説明しましょう。

コンピュータのプログラムは、情報を格納するための領域をメモリ上に確保します。特に、文字情報を格納する場合、その文字数に応じて連続したメモリ領域を確保します。このように、同じ形式のデータを複数個格納するためにメモリ上に確保する領域のことをバッファ領域と言います。

バッファへの情報の格納方法としては、ネットワークからの入力、ファイルからの入力、キーボードなどの入力デバイスを介してのユーザーからの入力などがあります。

 

 

●バッファオーバーフローの仕組み

しかし、言うまでもないことですがこのバッファーの容量には限界があります。バッファ領域の上限はプログラムが規定出来ても、プログラムを実行するCPUはバッファ領域の上限がわかりません。

そのため、情報をバッファ領域に格納する際、格納する情報の大きさがバッファ領域の上限を超えてしまうことがあります。すると、CPUはバッファ領域を超えて情報を格納してしまいます。これが、バッファーオバーフローです。

バッファオーバーフローが起こってしまうと、メモリ上の不正な場所に情報を格納することになってしまうため、プログラムが誤動作したり、ほかの領域に保存されている大事なデータを破壊してしまったりします。

 

 

●バッファーオーバフローによるセキュリティーホール

クラッカーは、このバッファオーバーフローをわざと起こしてデータの改竄・コンピュータシステムの損壊につながる操作をおこなうことから、ソフトウェアでバッファオーバーフローの脆弱性が発見されると、高い優先度で修正が行われ、更新バージョンのプログラムや修正パッチの公開・配布などが行われます。

それだけ、バッファーオーバフローによる攻撃はやっかいなのです。

 

 

●C言語とバッファーオーバーフロー

実はC言語は、このバッファーオーバフローが起こりやすい仕組みを持っている、という致命的な欠点を持っているのです。

例えば、C言語の標準入出力関数であるscanf関数やgets関数はバッファ長のチェックを行わないで標準入力をバッファに書き込むので、バッファーオーバーフローを起こしやすくなっています。

C言語の歴史はたいへん古く、インターネットが現在のように普及するはるか以前に作られた言語ですから、言語の仕様の中にそういった欠点があるのはある意味仕方がないのとかもしれません。そういったこともあり、C言語は新しいバージョンであるC11でこういった問題に対処するために、gets関数を排除するなどの対策をしています。

しかし、メールサーバーなどで用いられるsendmailと呼ばれるプログラムは、C言語でかかれ、古いライブラリ関数を多用していることから、頻繁に修正されていましたが、ついにはセキュリティ上の問題などでsendmailを標準プログラムから排除する動きがあり、いくつかのOSの標準セットからsendmailは取り除かれてしました。

 

 

●セキュアなコード

こういったバッファーオーバフローなどによる脆弱性への対策を行っているコードのことを、「セキュアなコード」と言います。またそのようなコードのプログラミングを行うことを「セキュアプログラミング」と言います。

セキュアなコードをかくための方法は大きく分けて二つあります。一つは、従来の枠組みのなかでできるだけ「セキュア」にプログラミングをする方法、そしてもう一つは「セキュアな関数」を用いてプログラミングを行う方法です。

たとえば、マイクロソフトは、自社が開発したVisualC++コンパイラで、危険性のあるscanfのかわりに、scanf_sという「セキュアな」scanfを用意して、バッファーオーバランを回避する工夫をしています。これにより、以前よりもセキュアなコードを作成しやすくなりましたが、「完全」な対策を行うことは不可能です。

ただ、セキュアなコードをかく方法に関しては、研究が進んでおり、ネットなどでも多くの情報を得ることができます。プログラマーは、そういった情報を絶えずチェックして、脆弱性の低いプログラム作りを心掛ける必要がります。

 

 

4.C言語とオブジェクト指向

 

●C言語は構造化プログラミング言語

このコラムのいくつかのトピックでもすでに取り上げたように、C言語というプログラミング言語は1972年に開発された言語で、ITという変化の激しい世界の中では非常に歴史の古い存在で、それだけ長く残ってきたことは偉大なことではありますが、どうしても「時代遅れ」の部分があることも事実です。

そもそも、C言語というプログラミング言語は、構造化プログラミング言語と呼ばれるもので、プログラム全体を段階的に細かな単位に分割して処理を記述していく手法です。この考え方は、1960年代後半にオランダの情報工学者エドガー・ダイクストラ(Edsger Wybe Dijkstra)氏らによって提唱されたもので、C言語はそれを具現化したものの一つです。

その考え方は、「一つの入口と一つの出口を持つプログラムは、順次・選択・反復の3つの論理構造によって記述できる」というもので、この原則に従うことにより、大規模なプログラムを効率よく、少ないミスで設計・記述できるようになりました。

そして、この原則に従うことにより、大規模なプログラムを効率よく、少ないミスで設計・記述できるようになるようになったので。そしてそれは、C言語が大きく成功した理由の一つでした。

 

 

●C言語の限界

このように、当時としては斬新な手法で開発されたC言語は、プログラミングのみならずコンピュータ関連技術の発展に大いに寄与しました。とはいえ、やはり時代の変化には抗しがたく、C言語にも限界が見えてきました。

C言語の欠点は、いくつか挙げることができますが、ここで最大の問題になったのが、「大規模開発に向かない」ということでした。

こういう言い方をすると、「それは、C言語が開発されたときのメリットの一つだったのでは?」と疑問に思う方もいるでしょう。しかし、C言語が出来た当時とその後とでは、「大規模」の「規模」が違ってきてしまうのです。C言語は確かに、「その当時としては」大規模開発に適した言語でした。しかし、C言語が可能にしたさらなる大規模開発には、「C言語では物足りない」という状況を創り出していったのです。なんとも皮肉な話です。

 

 

●オブジェクト指向の考え方の導入

そのように、限界にぶつかったC言語でしたが、この状況を打破する方法として考えられたのがオブジェクト指向という考え方の導入でした。オブジェクト指向とは、ソフトウェアの設計や開発において、操作手順よりも操作対象に重点を置く考え方で、データやその集合を現実世界のモノ(Object)になぞらえた考え方です。

例えば、我々が自動車を運転するとき、自動車の内部でどのような装置が動作しているかを理解する必要はありません。ただ運転方法だけを知っていればいいだけです。このように、個々のオブジェクトに対し、操作方法を設定することでその内部の難しい詳細を覆い隠し、利用しやすくしようとする考え方がオブジェクト指向です。

このオブジェクト指向を導入すれば、プログラマーはすでに用意された完成された「部品」を上手に組み合わせることにより、簡単にプログラムができるようになります。C言語とは無関係に開発されたこのオブジェクト指向という考え方ですが、ついにその考え方がC言語にも適用されるようになったのです。

 

 

●C++とObjective-C

C言語に対するオブジェクト指向への対応という拡張は、独自に2つの道がとられました。

一つは、最もポピュラーなオブジェクト指向言語の一つといえる、C++(シー・プラス・プラス)です。1983年にベル研究所のコンピュータ科学者のビャーネ・ストロヴストルップによって開発されたもので、当時の名前は「C with Classes」でした。

C++は、Simulaという当時使われていたオブジェクト指向言語の特徴をとりいれたもので、C++という名称の由来は、C言語に存在する算術演算子の一つで、整数型の変数の値に1を加えるインクリメント演算子「++」から来ているといわれています。「C言語より1つ進歩した」といったl気持ちなのでしょうか。

そして、もう一つが、Objective-Cです。Objective-Cは、1983年にブラッド・コックスによって開発され、そのコンパイラやライブラリを支援するためにStepstone社を創立しました。しかし、しばらくはマイナーな存在でしたが、1985年にアップルコンピュータを去ったスティーブ・ジョブズが、この言語に着目し、彼がNeXT Computer社を創立した際には、その製品であるNeXTコンピュータの主力言語となりました。

のちにジョブズがアップル社に復帰すると、この言語は、MacOSXや、iOSのソフトウェア開発に用いられるようになり、現在では、「アップル製品用のオブジェクト指向言語」として普及するようになりました。

 

 

●その後の後継言語

前述の二つの言語、C++や、Objective-Cの特徴は、C言語のソースコードがそのまま使える、ということでした。つまり、それまでのC言語の資産を継承しつつ、オブジェクト指向プログラミングが可能である、という点が最大の特徴でした。

その後、オブジェクト指向が、プログラミングの一般的な考え方となると、C言語の記述方法などは継承しつつ、C言語とは全く違ったオブジェクト指向言語が現れ、普及するようになりました。

そのなかでもメジャーなものの一つが、Javaで、C言語/C++言語の文法を参考にしつつも、ポインタ等の低レベルな操作は基本文法から排除されており、仮想マシンの上で動作し、これにより、プラットフォームに依存しないアプリケーションソフトウェアが開発できるというC言語にはなかった特徴を持っています。

また、マイクロソフトが開発したC#言語もまた、C言語やC++言語を継承しており、これら言語の文法の利点を継承しつつ、独自の拡張がなされています。言語の開発に従事したアンダース・ヘルスバーグは、「C#」が「C++++」(すなわち「C++をさらに進めたもの」)にみえるのが由来、と語っており、このことからも、この言語がC/C++言語の後継言語であることを意識していることがわかります。

さらに、アップル社も、Objective-C言語の後継言語として、Swiftという言語を開発しました。このように、オブジェクト指向もいわば「第二世代」に入り、残念ながらC言語はますます肩身が狭くなりそうです。