初級C言語Q&A(1)

初出: C MAGAZINE 1995年6月号
Updated: 1996-03-12

[1つ前] [1つ後] [質問一覧] [記事一覧] [ホームページ]


プロローグ

 ネットでC言語に関する会議を見ていると、同じような質問が何度も繰り返して出現することがわかります。 この種の質問は、俗にFAQ(Frequently Asked Question)と呼ばれます。

 同じ質問が何度も繰り返される理由を考えること自体、興味深い問題ではあり ますが、実際にこの種の質問を見た限りでは、大半の疑問に対しては「疑問を持 つのももっともだ」と感じざるを得ないものがあります。C言語は多数のOS上で動 作しています。この結果、C言語は多くの人が使うようになりましたが、各処理系 によってふるまいが違うことも多くなるという欠点があり、それに対する疑問は C言語の仕様だけでは解決することができません。このことがFAQに拍車をかけて いるようです。例えば「画面をクリアするにはどうすればよいか」のような疑問 は、C言語の知識だけでは回答不能の、いわば、落とし穴のような問題だといえま す。しかし、「本質的にこのような問題はC言語とは無関係である」という理由が あるため、初心者向けの本にはあまり詳しいことは書かれていないようです。そ の結果、質問が何度も繰り返される、という悪循環が発生することになります。

 というわけで、C言語をマスターするために必要な、しかしあまり本には書かれ ていない、この種のありきたりな質問を目についたものからピックアップしてみ よう、というのがこの企画です。回答は個人的な主観も混じりますので、異論も あるかもしれないし、常に解決方法が一つというわけでもありません。その場合 は「このような考え方もある」という程度に理解していただければ幸いです。


コンパイラに関する話題

Q1 【何が必要か】

 C言語は全く知らない。これから勉強したい。どのような環境を揃えればよい か。コンパイラは何を使えばよいか。

 C言語が世の中に出て来た頃には、C言語はミニコンの上で動作する重い処理系 でした。現在出まわっているパソコンの性能は、当時のミニコンよりもはるかに 高く、C言語の処理に十分な能力を持っています。また、性能を生かしたコンパ イラも市販されており、安価で購入できるようになりました。

 C使いへの道は、はてしなく遠いものです。他の言語に熟練している人ならよい のですが、プログラミングの経験がないと、最悪の場合は挫折することもありま す。市販コンパイラは手の届かない価格ではありませんが、買っても使わずに飾 るだけというのはもったいない話です。かといって、市販のソフトウェアを誰か にコピーしてもらったりするのは著作権法に違反するおそれがあります。

 そこで、まずC言語とはどんなものか実際に試してみたいという段階なら、無料 で配布されているコンパイラがいくつかあるので、これを使うという手をおすす めします。

 もしMS-DOSを使うという条件があれば、LSI C-86というコンパイラの試食版が 有名です。ネットにも登録されているので、ダウンロードすることもできますが、 データのサイズが大きいので結構時間がかかります。雑誌等に付録で付いている ものを探して使った方が便利だし安上がりになる場合があります。このプログラ ムは、条件を守った上でコピーすることが許されているので、既に持っている人 が身近にいれば、コピーしてもらっても構いません。

 MS-DOSに関する知識に自信のある方なら、djgccを使う手があります。これは gcc(GNU C Compiler)をDOS上で実行できるようにしたものです。これを使う場合 は、go32というメモリ管理プログラムを併用する必要があります。C言語以外の 知識が必要という点では、チャレンジ精神が余計に必要となるでしょう。

 MS-DOSにこだわらないで、かつ、必ず上達するのだという強い決意を持ってい るのであれば、フリーのUNIXを使う手をおすすめします。FreeBSDやLINUXには、 gccが添付されて配布されています。これはCだけでなくC++もコンパイルできるコ ンパイラです。ただし、一般論として、そもそもこれらのUNIXをインストールす るのが大変です。運が悪ければそんじょそこらの知識ではダメかもしれません。 パソコンの構成がたまたま標準的なものであれば、運良く何も考えなくても動作 することもあります。

 なぜ、この方法をお薦めするかというと、これらのパッケージには大量のソー スコードが付いて来るからです。FreeBSDやLINUXそのものがCで書かれています。 中にはとんでもないプログラムもありますが、概してこのように配布されている ものは、ある程度のレベルに達しているものです。

 経済的に余裕があれば、市販のコンパイラを買うという選択肢もあります。


Q2 【パソコン】

 どのパソコンを買えばよいか。

 C言語を勉強するのが目的なら、基本的にパソコンは何でも構いません。なぜな ら、Cコンパイラが使えないパソコンというのは滅多にないからです。個人的なア ドバイスとしては、C言語を使うのが主な目的なのであれば、とりあえずは安いも のがいいと思います。安いといっても使い物にならないのでは困りますが、IBM-PC 互換機(いわゆるDOS/Vパソコン)の、少し古いモデルが、比較的安く購入できる のでよいと思います。

Q3 【VC++とBorland C++】

 VC++とBorland C++のどちらかを買おうと考えている。どちらがよいか。

 これは難問です。実は私はVC++を使ったことがないので比較できませんが、両 方使ったことのある人の意見を見た限りでも、難問のようです。この質問に対す る回答はこのコーナーの範囲を超えています。

 市販コンパイラを選ぶ場合に注意すべきことが、いくつかあります。

  --- * ---
 ・あなたの持っているマシンで動作するか。
 MS-DOSのバージョンが古すぎて…ということは、まさかないと思いますが、場 合によってはMS-DOSも新たに買う必要があります。Windowsがなければインストー ルできないコンパイラもあります。メモリが最低必要とされている以上になけれ ば動作しないし、環境によってはより多くのメモリが必要となります。これは製 品のカタログに書かれているので、購入前に確認してください。
 ・サポートはどうか。
 ユーザーサポートが有料化される傾向にあります。どのようにすれば、どの程 度のサポートが受けられるのか、ということを押さえておく必要があります。ユ ーザーサポートが有料だと良くない、というのは間違った発想です。問題はその 内容です。無料であっても役に立たないレベルのサポートは無意味だし、有料で もその価値のあるサポートなら元はちゃんと取れます。
 ・参考書はあるか。
 最近のコンパイラは、チュートリアルといって、C言語を勉強するためのテキス トと練習用のプログラムが付いていて、実際に例をコンパイルしながら身に付け ることができるように工夫されています。これを実際に行うのが上達の近道です が、どうしても分かりにくいという場合には、他の参考書が市販されていると便 利かもしれません。
 ・ネットで情報交換できるか。
 意外と便利なのがネットです。最新バージョンの情報、雑誌には載らないよう な細かいバグ情報や、ユーザーが実際に体験した「よくある勘違い」の情報、バ グ修正の差分データなど、ネットから迅速に入手することができるようになりま した。メーカーがSIG、フォーラム、ホームページ等を主催しているかどうかが ポイントになります。
 ・コンパイラの性能はどうか。
 コンパイラの性能としては、生成するコードの量、実行時の速度と、コンパイ ル自体にかかる速度が重要です。一般論としては、良いコードを出すものほどコ ンパイルに時間がかかる理屈になりますが、そう単純な話ではないはずです。
  --- * ---
 さて、VC++とBorland C++を比較すれば、おそらく上記のような条件は五分五分 のはずです。従って難問になるわけです。

Q4 【gcc】

gccというのは何か。

 Free Software Foundationという団体が行っているプロジェクトに、GNUという 有名なものがあります。このプロジェクトは、高い品質の(?)フリーソフトウェア を世界中に配布しようというものです。gccは、GNUが配布しているC++コンパイラ で、サブセットとしてC言語のプログラムもコンパイルすることができます。

 gccは基本的にUNIX上で動作しますが、数多くのCPU、そして処理系に移植され ています。DOS上で動作するものとしては、djgccと呼ばれるものが有名です。

 gccは多くの資源、特にメモリを必要とします。最近のパソコンで、Windowsが 動作する程度のメモリがあるものなら動作は問題ありませんが、少し前にあった ような640KBのメモリ、80286以前のCPU、といった環境だと動作させることはでき ません。

gccは、世界中のフリーソフトウェア利用者から報告されたバグを修正したり、 また、新たな機能を追加したりして、徐々に進化している最中です。従って、バ ージョンアップは割と頻繁にあると考えてよいでしょう。現在のバージョンは、 2.6.3あたりだったと思います(1996年6月現在)。gccのバージョンは、このよう にピリオドで区切った3つの数字で表現し、左から右に向かって小さな変更によ るバージョン変更を意味することになっています。

GNUに関するタイムリーな情報は、インターネットのニュースが読めるならgnu というニュースグループを、そうでない場合にはUNIXに関連するニュースグルー プ、あるいは各パソコン通信のUNIX関連のsigが参考になります。


参考書

Q5 【入門書】

 C言語は全く知らない。入門のための本は何がよいか。

 いまやC言語に関する入門書が、多数出版されています。どれがよいかという のは、全てを読んだわけではないので、コメントできる立場ではありません。

 ただ、ネットでの意見を参考にした限りでは、どの本がいいというのはともか く、「この本だけは止めておけ」という本はあるようです。とはいえ、ここでそ れを書くのはちょっと気がすすまないので、というとかえって知りたいと思われ るかもしれませんが、とりあえず知りたい人はネットで誰かに聞いてみるか、今 までの議論のログを見てみるとよいと思います。私見としては、「これは止めて おけ」と言われた本は、忠告通り止めておいた方がいいと思います。

 パソコン用に販売されているCコンパイラには、練習用のプログラムやマニュア ルが付いているものもあるので、それを使うのも一つの手です。

 パソコン通信などにアクセスできる人なら、C言語に関するsig、フォーラム、 あるいはインターネットのニュースグループを見るのも勉強になります。


Q6 【K&R】

 K&Rとか、K&R 2nd.というのは何か。

 カーニハン&リッチーによる「プログラミング言語C」という本で、C言語 のバイブル的存在として最も有名です。略記してK&Rと書かれます。第一版 を大幅に改定した第二版が現在広く読まれていて、これはK&R 2nd.と略記さ れます。とりあえず持っていると安心できるという意味では持っていて損のない 本ですが、翻訳の質に関して批判的な声もあり、原書を読めという人もいます。 個人的意見としては、英語の方が得意という方ならともかく、そこまで固執する よりも、和訳を読んだ上で、さらに実際にCのプログラムを読んだ方が現実的で しょう。和訳を買う時には「第2版」というの選んでください。

 ただし、他のプログラミング言語の経験や、一般的なプログラミングに関する 知識、さらにはUNIX等のOSの知識が多少なければ、理解は困難かもしれません。 初心者の方は、この本を買って、読んだがさっぱり分からない、というのでも、 落ち込む必要は全くありません。


処理系依存の問題

Q7 【処理系依存とは何か】

 「その質問はシステムに依存する」とはどういう意味か。

大雑把にいえば、C言語の仕様だけでは回答できないということです。言い換 えれば、コンパイラやOSによって、その回答が異なる類のものであるということ です。

C言語の構成を大きく分類すると、二つに分けることができます。まず、言語 の仕様そのもの、すなわち、変数はどのように定義するとか、ループを実現する にはどんなキーワードを使うとか、どのような演算子があるか、ということがC 言語の仕様として決まっています。もう一つは、標準ライブラリと呼ばれている ものです。C言語は、関数という処理の単位を組み合わせることによってプログ ラムを作成します。プログラマーは必要な処理を、言語仕様に従って関数を作成 するという作業によって実現します。こうやって一から関数を作るのは大変な作 業になるので、多くのシステムで共通的に使われる処理は、最初から決められた 仕様の関数が用意されていて、これを標準ライブラリと呼んでいます。

 C言語には、このように関数を後から追加することにより全体の処理を構築でき るという特徴があります。入出力の処理は、他の言語処理系では、言語仕様の中 に含まれることがありますが、例えば、C言語の場合は関数として実現されている ため、関数の中身を入れ替えるだけで他の処理系で動作できる可能性もあります。

 しかし、システムによって実現の方法がまちまちな処理が多いという現実もあ ります。C言語は、このようなシステムに関連する処理を、言語自体としては関知 せず、処理系に専用の関数群をシステム毎に用意することによって解決していま す。いわば標準でないライブラリです。

 このような処理は、C言語の仕様を超えた内容なので、質問に対してC言語の知 識だけでは回答することができません。回答するためには、その処理系に附属し ているライブラリの知識が必要になるのです。

(参考) Q&A(1)-8【処理系依存の問題】 ,Q&A(9)処理系依存の問題(全文)


Q8 【処理系依存の問題】

 処理系に依存した「よくある質問」には、具体的にはどのようなものがあるか。

 例えば「画面を消去するにはどうすればよいか」「キーボードのエコーバック を表示しないで入力するにはどうするか」「キーバッファに文字が入っているか どうか知る方法は」「マウスの情報を知るにはどうすればよいか」のような質問 です。

 これらの質問に回答するためには、どのような環境でそれを行うかという条件 が必要になります。


Q9 【不定】

 動作が不定である、というのはどういう意味か。

 簡単にいえば、C言語の仕様としては、結果が決まっていない、という意味で す。典型的なのが、演算子の評価順序です。

例えば、

    printf("1") + printf("2");
 の結果は、
    12
 かもしれないし、
    21
 かもしれません。C言語の入門者は、足し算の左右どちらが先に実行されるか 分からないという事実に驚くかもしれませんが、実際、どちらが先に実行されて もC言語の仕様には違反しないのです。

 動作が不定な場合も含め、基本的に処理系に依存するようなコードは書くべき ではありません。たまたま今あなたが使っている環境では思った通りに動くかも しれませんが、コンパイラをバージョンアップしてコンパイルしただけで、思っ た通りに動作しないことがあり得るからです。その場合にもコンパイラには一切 責任はありません。

 ふるまいが不定であるようなコードは、たいてい、わずかな修正を行うだけで、 処理系に依存しない明確な処理に書き直すことができます。この場合、確実に12 と表示させたいなら、

    printf("1");
    printf("2");
 のように書けばよいのです。これが12という表示結果になることは、C言語で は保証されます。もちろん、次のように書いても期待通りの結果になります。
    printf("12");

 このように、多くの場合、動作が不定であるコードは、ほんの少しの修正で仕 様に定義されたコードに変更可能です。しかし、 エンディアン の問題のように、効率を無視しなければ簡単には解決できない場合もあります。

(参考) Q&A(7)-1【不定】


Q10 【エンディアン・アライメント】

 あるマシン上で作ったデータを、他のマシンで読もうとしたら、値が全然違う ものになっていた。プログラムはCで書かれていて、全く同じものを別々のマシン でコンパイルしただけで、内容は全く同じなのだが、何がいけないのだろうか。

 一つの可能性としては、多分、次のようなコードがプログラムの中にあるのだ と思われます。
    write_int(int i, FILE *fp)
    {
        fwrite(&i, sizeof(int), 1, fp);
    }
 このような書き方は、エンディアン(endian)とアライメント(alignment)に対し て問題を引き起こします。

 エンディアンとは、マシン上で整数値を格納する時に、上位から格納するか下 位から格納するかということです。例えば0x1234という値をメモリ上に1バイトず つ格納する場合に、0x12、0x34の順に1バイトずつ格納するマシンと、0x34、0x12 の順に格納するマシンがあります。前者をビッグエンディアン、後者をリトルエ ンディアンと呼びます。 fwrite は、メモリ上に保存されているデータのイメージをそのままファイルに書き出し ます。ビッグエンディアンのマシンで作成したデータをリトルエンディアンで読 むと、0x1234と書いたつもりなのに、読んだ時には0x3412となってしまうでしょう。

 アライメントの問題は、主に構造体のデータをそのまま fwrite を使って一度でファイルにしようとした時に発生します。構造体の多きさは、処 理系によって異なるかもしれないからです。

 これらの問題を避けるためには、2バイト以上の大きさを持つデータは、数値を アスキーで表現するか、あるいは fwrite を使わずに putc などを使って1バイトずつ確実な順番でファイルに書き込むのが確実です。 例えば long の変数 l は次のように処理します。

	putc((l >> 16) & 0xff, fp);
	putc((l >> 8) & 0xff, fp);
	putc((l & 0xff, fp);
 これは fwrite を使って一度に書くよりも、はるかに冗長なように見えますが、ディスクに書き 込むという処理は実際にディスクに書き込みをする時間の方が問題にならない位 長くなることが多いので、プログラムの実行、処理の時間としては、それほど致 命的にはなりません。

 構造体のデータを書く時には、それぞれのフィールド毎に上のような手順で1バ イトずつ書き込む関数を呼び出すとよいでしょう。 sizeof(STRUCT) は、パディングを含んだサイズになるので、必ずしも各フィールドのサイズの合 計に等しいとは限らないことに気を付けてください。


Q11 【intのサイズ】

 エンディアンが同じマシンである別のマシンにプログラムを移植したら、動作 がちがうようだ。

 ハードウェアの相違でありがちなのが、 int のサイズの問題です。DOSでよく使われるCコンパイラでは、 int のサイズは16ビットですが、UNIXなどのプログラムでは、 int のサイズが32ビットであると想定してあるものがあります。この種のトラブルを 避けるには、 int の変数を使う時には-32767〜32767の範囲に値が収まるように留意し、これを超え るならば long の変数を使うようにするとよいでしょう。

(参考) Q&A(5)-1【intの扱える値の範囲】


Q12 【testが動かない】

 次のプログラム"test.c"をコンパイルして生成された"test"というコマンドを実行したが、画面には何も表示されない。
    #include <stdio.h>

    main(void)
    {
        printf("hello, world¥n");
        return 0;
    }

 UNIXにはtestという名前の標準コマンドがあります。実行されているのは、今 コンパイルしたtestではなく、標準コマンドの方だと思われます。./testを実行 してみるか、別のファイル名にしてからコンパイルして、実行してみましょう。

Q13 【非標準関数】

 strrev という関数がみつからない。

strrev という関数はANSI Cには定義されていません。ある関数が標準関数かどうかは、 K&R 2nd.を見るか、あるいはコンパイラのリファレンスマニュアルを見れば 判断できるはずです。

 しかし、歴史的に割とよく使われる関数のようなので、必要なら、次の関数を どこかに追加して一緒にコンパイルしてください。

 ただし、通常の用途において、文字列をひっくり返す必要は殆どないはずです。 strrev を使う前に、本当に strrev が必要なのか、アルゴリズムを見直した方がよいと思います。元の文字列が意味 のある内容であれば、 strrev した結果を最終的に使うためにはもう一度 strrev に相当する処理を行う必要があります。配列やポインタを使えば文字列の内容は そのままで、後ろから処理することはそう難しいことではないはずです。

 なお、次の関数は2バイト文字には対応していません。また、一般に strrev という関数は文字列を単純に1バイト単位で入れ替えるだけのものです。

#include <string.h>

/* reverse string
 * No Copyright
 */
char *strrev(char *s)
{
    char *p;
    char *q;
    char tmp;

    if (s != NULL) {
        p = s;
        q = s + strlen(s) - 1;

        while (p > q) {
            tmp = *p;
            *p = *q;
            *q = tmp;
            p++;
            q--;
        }
    }
    return s;
}

(C) 1995-1996 Phinloda, All rights reserved
無断でこのページへのリンクを貼ることを承諾します。問い合わせは不要です。
内容は予告なく変更することがあります。