このエントリーをはてなブックマークに追加
スポンサーリンク

コメントの大切さ

コメントとは

コメントとは、プログラムのソース中にある、実行しない行の事です。コメント行というのはプログラムの動作にまったく影響しません。コンパイラならコンパイルしないし、インタープリターならその箇所を評価しません。ですので、コメント行というのはプログラムの動作にとってまったく不要なものです。極端な話、コメント行が一切ないプログラムでも動作します。

じゃあ、なんでそんなものがプログラム中に存在するのでしょう?

プログラムの「ソース」というのは、コンピューターが解釈しやすい言語です。つまり、人間にとって解釈しにくい言語です。ですから、人間が「ここは、こういう事をしているんですよ」とソースに注釈を入れます。コンピューターにはそんな注釈理解できませんので、その箇所は読み飛ばしてもらいます。それが「コメント」です。

慣れてくると下手に言葉で解説されるよりソースの命令を直接読んだ方がわかるようになってきます。ですので、「オレはコメントなんかつけねーよ」と言うかもしれませんが、それでもコメントを入れるほうが良いと思います。それは以下の理由によるものです。

(1)共同作業である
(2)自分が忘れる場合がある
(3)徹夜明けや修羅場かもしれない

(1)は、特にPHPの場合ですが、1つの目的のためにさまざまな書き方が存在する場合があるという事です。echo "…"と書く人もいればprint "…"と書く人もいるでしょうし、私のように、printf("…") と変数が混在しない場合でもprintfを使う派(?)がいるかもしれません。変数を混在させるにしても、 printfを使うとか、print 文中に、ピリオドで変数を足すとか、色々方法があると思います。

このような場合に、他人の書いたソースを解読するのに時間がかかると、それだけコストがかかってしまいますから、他の人にもわかるようなソースにした方が良いでしょう。

(2)他人のソースがすぐに解釈しにくいのは当然ですが、あまりに長年やってると、もう昨日作ったソース・・・下手すると、ついさっき作ったソースも忘れてしまう場合があります。(もっとも、そうなるとかなりヤバイですが)、特に長くなってくると最初の方がどうなっていたか自分でもわからなくなってしまう事がしばしばありますから、忘れそうな所はコメントを入れておきます。

(3)は、あまり考えたくない事ですが、納期が今朝とか、下手をすると昨日中という場合があります。この場合、大抵は客先に「今夜中にやっておきます」とか「もう出来ていて、今は最終チェックの段階です」と告げます。そして、客先の担当者が起きて出社してくる前に仕上げるという泥縄な状態になりますが、寝不足と疲労で脳みそがほとんど機能しなくなる場合がほとんどです。そんな時のためにも、体力・睡眠時間に余裕があるうちに、ソースを読むのにできるだけ頭を使わなくて済むようにしておきます。

コメントの例

まずはエラー処理入門のところでも使った、このサンプルをみてください。
/*バッファ確保*/
if (NULL == ( buff=malloc(256))) {
    fprintf(stderr,"Out of memory\n");
    return -1;
}

bzero(buff,256);

/*ファイルオープン*/
filename="test.dat";
open_mode="r";
if (NULL == (fp=fopen(filename,open_mode))) {
    fprintf(stderr,"File cannot open.\n");
    return -1;
}

/*EOFに達するまでループ*/
while (1) {

    /*1行ファイルリード*/
    if (NULL == (fgets(buff,256,fp))) {

    /*ファイルがEOF*/
    if (feof(fp)) {
        break;
    }

    /*ファイルリードエラー*/
    fprintf(stderr,"File read error.\n");
        return -1;
    }

    printf("%s",buff);
}

/*ファイルクローズ*/
if (EOF == (ret=fclose(fp))) {
    fprintf(stderr,"File cannot close.\n");
    return -1;
}
これは、エラー処理入門のところで使ったソースです。このように、1命令ごとにくどいほどコメントがついています。特に/*ファイルオープン*/など、「見ればわかるだろ」というツッコミが聞こえてきそうなほどに。

それでも、時間に余裕があるのであれば、なるべくコメントは入れておいた方がベターです。コメントを多めに入れる事で、万一思い通りに動かなかった場合にも、コメントがあると頭の整理に役立ち、問題の解決を早めます。実際、このサンプルプログラムも、一発で何のエラーもなくすんなり動いたわけではなく、幾度かコンパイルエラーや、セグメンテーションフォルトを出してしまいました。しかし、これだけコメントがついていると、問題が発生してもデバッガを起動することなく、どの辺に問題があるか一発でわかりました。

コメントは他人にとってわかりやすくするためだけでなく、自分が読んでわかりやすくすることで、己自身をも救う事になると思います。

ソースは本当の仕様書

プログラムを作る前には(当然ですが)まず「仕様」を決めます。

筆者のような中小のプログラマは、大きい会社の大きいプロジェクトの関数の一部を作るという仕事がよくあります(って自分だけ??)この場合は特に「仕様書」は大切です。大勢の共同作業では、仕様の見解が一致していないと、いざ全員の作った関数、オブジェクトをリンクしようとして全然動かなかったという事にもなりかねません。なので、大きいプロジェクトほど大量の仕様書が作られます。プロジェクト参加者全員がこの仕様に基づいてプログラムを作っていくわけです。

・・・というのはタテマエで、実際には大抵仕様は二転三転します。最初のうちは分厚い仕様書もその都度修正するのですが、そのうちスケジュールが切羽詰ってくると仕様書の変更以前にまずプログラムを修正するようになります。また、もう本稼動が始まってからの仕様変更は緊急の場合が多く、仕様書の変更は後回しになる事がほとんどです。

という事をしていくうちに、仕様書に記載された内容は現実とどんどん離れていってしまいます。特に初版の仕様書は実際に稼動している仕様と全然違っていることでしょう。なので、最も信用できる仕様書はソースであると言えます。下手をすると、システムが安定して仕様変更もなくなってきた頃に、ソースを見ながら仕様書をおこしなおすケースもあります。

そのためにも、ソースにはコメントをより多く入れておくのがベターです。

関数の引数と出力

関数とは、引数を与えると決まった処理をして結果を返すというものです。どんな大きなプログラムを作る場合でも、まずは小さな関数をいくつか作っていき、それを組み合わせて徐々に大きなプログラムにしていきます。

関数は、汎用的にどんな場面でも使いまわせるのがベターです。つまり、この関数は何をするもので、どんな引数を与えるとどういう処理をして、どんな結果を返すのかを明確にしておき、色々な場面で同じ関数が使えるようにする事で、似たような処理をいくつも作る必要がなくなり、作業効率がアップします。

例として、これは引数で指定された文字列(へのポインター)を渡すと、その文字列の長さ+ヌルバイト分のメモリーを確保した後、中身をコピーして確保したメモリーのポインターを返す関数です。
/*
=============================================================================

        引数で指定された文字列を格納するだけのメモリを動的に確保した後、
        中身を転送して、ポインターを返す。

        引数 char *a_moto 移したい文字列
        返値 NULL以外:確保した場所のポインター
             NULL    :失敗

=============================================================================
*/
char * char_malloc(char *a_moto) {
     int nagasa;
     char *mojip;
     char *a_saki;

    /*元の長さを測って、NULLコードが入る分上乗せする*/
    nagasa=strlen(a_moto);
    nagasa++;

    /*メモリ確保*/
    if (NULL == ( a_saki=malloc(nagasa))) {
          fprintf(stderr,"RTemu:out of memory\n");
          return NULL;
    }
    
    bzero(a_saki,nagasa);

    /*  コピー */
    mojip=strcpy(a_saki, a_moto);
    return a_saki;
}
関数の最初に、この関数が何をするものかを説明しています。続いて、引数の説明と、リターンコードの説明があります。リターンコードも、エラーの場合とエラーでない場合それぞれの説明があります。このように関数にはそれぞれ、

・処理の内容
・引数
・リターンコードの意味

の説明を入れておくことで、他の人がこの関数を使うための手助けになり、同じ処理をする関数を作らずに済みます。それにより、プロジェクト全体の作業効率がアップします。

ソースの読みやすさはすべてに優先する

これは、pg_csvのソースの一部です。
/*CRコード*/
if (moji1==0x0d) {
        *(kekka2+cnt2)=0x5c;
        *(kekka2+cnt2+1)=0x72;
        cnt2+=2;
        seigyoflg=1;
}

/*LFコード*/
if (moji1==0x0a) {
        *(kekka2+cnt2)=0x5c;
        *(kekka2+cnt2+1)=0x6e;
        cnt2+=2;
        seigyoflg=1;
}

/*タブコード*/
if (moji1==0x09) {
        *(kekka2+cnt2)=0x5c;
        *(kekka2+cnt2+1)=0x6e;
        cnt2+=2;
        seigyoflg=1;
}

/*¥マーク*/
if (eucenen==1 || sjisenen==1) {
        if (moji1==0x5c) {
                *(kekka2+cnt2)=0x5c;
                *(kekka2+cnt2+1)=0x5c;
                cnt2+=2;
                seigyoflg=1;
        }
}

/*制御コードじゃない*/
if (seigyoflg==0){
        *(kekka2+cnt2)=moji1;
        cnt2++;
}
この処理は、データーベースから読み込んだデーターのうちの、制御コードを文字列に置き換える部分です。CRコードなら\r 、LFコードかタブなら\n、¥マークなら¥¥という風に置き換えた後、制御コード以外なら文字そのものをポインターの示すメモリーに入れて次のループへ行きます。

本来ならば、置き換えの必要な制御コード(つまり0x00〜0x19)に相当する変換テーブルを作っておいて、0x19以下のコードがきたらテーブルに基づいて置き換え後のコードをポインターの示す場所に追加すると思います。また、ここでは制御コードがきたらseigyoflg=1とフラグをたてておいて、最後にif (seigyoflg==0)というようにフラグで判別していますが、これもif {} else {}を使えばフラグを使わなくて済むはずです。

ところで、ダイの大冒険という漫画でミストバーンというキャラがこう言いました。「大魔王様のお言葉は全てに優先する」と。同様に(どこが同様だ!?)、ソースの読みやすさは全てに優先します。if文の連発やフラグによる処理の切り替えは、見ていて稚拙な印象を受けます。しかし、最高に読みやすいです。変換テーブルを作った場合、それを解読するためにはその変換テーブルをエディタの別画面で開かねばなりませんが、if文の連発であれば、この部分だけを見ればこの部分が何をしているかわかります。

さらにフラグを使う事により、
/*制御コードじゃない*/
if (seigyoflg==0){
という箇所が、「これは制御コードが来なかった時の処理ですよ」というのが見てわかりやすくなります。さらに、
if (eucenen==1 || sjisenen==1) {
   if (moji1==0x5c) {
のように、eucenenフラグやsjisenenフラグが立っていた時に限り¥コードを置き換えるという、複数の条件が後から発生した場合にif文の連続という原始的な作り方をしていた方がかえって融通が利きます。つまり、仕様変更に強い作りとなるわけです。

といったように、稚拙で一般的によくないとされる作り方も、可読性という面ではかえって都合が良い場合もあるという事です。

言語別コメント文のつけ方

コメント文のつけかたは、各言語まちまちです。

・COBOL
      *========================================================================
      *		改ページ
      *========================================================================
	PKAIP.
		IF	KA11 = 0
			MOVE 最後 TO YOKOBUF
			CALL YOKOGAKI USING YOKOPARA.
		CALL "KAIP53SE.BIN".
	PKAIP-EN.
		EXIT.
      *========================================================================
      *		用紙換え
      *========================================================================
	PGAE.
		IF	KA11 = 0
			GO TO PGAE-EN.

COBOLの場合、7カラム目に*を入れるとコメントになります。また、段落名(要するにラベル)は、タブ1個、命令はタブ2個、IF文はさらにタブを1個ずつずらして書くようにすると可読性が高まります。

・アセンブラ
;------------------------------------------------------------------------------
;              シフトJIS  →  JIS
;  IN  CS:[KANJI_1]  CS:[KANJI_2]   OUT CS:[KANJ_1]  CS:[KANJI_2]
;------------------------------------------------------------------------------
CONVJIS		=	$
		MOV	DH,CS:[KANJI_1]
		MOV	DL,CS:[KANJI_2]
		MOV	CS:[SHIFT_1],DH
		MOV	CS:[SHIFT_2],DL

		CMP	DH,0E0H
		JB	JSHIFT1
		SUB	DH,40H
JSHIFT1:	SUB	DH,71H

アセンブラの場合、先頭に;をつけるとコメントになります。アセンブラ言語は、とても可読性が低い言語です。というより、コメントがなければ何をやっているのかサッパリわかりません。大抵はレジスタとメモリを出し入れしたりI/Oポートを読み書きする処理の連続ですが、そのメモリーやI/Oポートの操作がどういう意味になるかというのは、解説文なしで後から解析するのは困難を極めます。

アセンブラ言語では、各サブルーチンごとに、レジスターの役割や使うワークエリア、できればセグメントレジスターをどうするか(保存する、とか破壊されるとか、あるいは、ESに何かをセットする必要があるとか)も書いておきましょう。

・Perl
# 背景イメージのある場所を指定
$bgimg = "/home/aufheben/public_html/cgi-bin/images/".$in{"bgimg"};

# カウント数のある場所を指定 (777にしておく)
$hdir  = "/home/aufheben/public_html/cgi-bin/data";

# FLYのある場所を指定
$flyprog = "/usr/local/bin/fly";

#文字イメージのあるディレクトリを指定
$digit_dir = "/home/aufheben/public_html/cgi-bin/images";

#gifappendのある場所を指定
$gifappend = "/usr/local/bin/gifappend";
Perlは、#を書くとそれ以降がコメントになります。Perlは、短く書けるものは、できる限り短く書くという風習があるため、可読性はあまり良くありません。そのため、できる限りコメントはつけるようにしましょう。

PerlはCGIで使われることが多く、掲示板やカウンター等のCGIのソースを公開して、利用者に自由にカスタマイズしてもらうという事も可能ですが、そのためにも初期設定のところにはすべてコメントをつけて、どの変数に何を代入しているかをわかるように書くと良いでしょう。
・PHP
        //------------------------------------------------
        //      入力が適切かどうかチェック
        //------------------------------------------------
        if ($Submit=="登録" || $Submit=="修正") {

                //文字数制限オーバーか?
                $message_length=strlen($message);
                if ($message_length > $MessageMax) {
                        printf("発言の文字数が多すぎます。<br>\n");
                        printf("ブラウザの【←戻る】で戻ってください<br>\n");
                        printf("</BODY>\n");
                        printf("</HTML>\n");
                        pg_close($handle);
                        exit();
                }
PHPの場合、//をいれると、そこからコメントになります。また、C言語と同様、/* */も使えます。(C++と同じですね)。

PHPは比較的可読性が良い言語です。コメント文なしでもソースをざっと見ていけば何をしているかわかるので、コメントはそんなにつけなくても良いのですが、それでも多くつけて悪い事はないでしょう。

・JavaScript
//入力のどこかに誤りがないかチェック
function InputCheck(form) {

        var aibo_no;
        var free_icon;

        if (!InputCheck2(form)) {
                return false;
        }

        aibo_no   = form.aibo_no.options[form.aibo_no.selectedIndex].value;
        free_icon = form.free_icon.value;
        if (aibo_no=="free" && free_icon=="") {
                alert("相棒アイコンのあるURLを指定してにゅ");
                return false;
        }

        if (form.passwd.value=="" && form.message.value!="") {
                alert("修正・削除パスワードをいれてにょ");
                return false;
        }

        return true;
}

//チェックボックスが2箇所以上チェックしてないかチェック
function InputCheck2(form) {

        var check_suu;
        check_suu=eval(0);

JavaScriptは、C++やPHPと同じ//がコメントになります。

JavaScriptも、コメントは多い方がいいかもしれませんが、ブラウザの「ソースを表示」で見えてしまいますから、あまり懇切丁寧にコメントを入れてるとソースを見られたときに恥ずかしいというデメリットもあります。

ただ、
<SCRIPT TYPE="text/javascript" SRC="javascript.js"></SCRIPT>
みたいにして別ファイルを読み込むようにしておけば、ソース表示ですぐに出てこなくなるので恥ずかしさも半減します。また、拡張子jsをブラウザで表示できないように、apacheを設定できるのであれば、JavaScriptのソースが読まれることもないので、思う存分コメントが入れられます。

・C言語
C言語は、最初のサンプルにもあるように、/**/で囲まれた部分がコメントになります。最近ではCコンパイラであってもC++みたいに//のコメントが使えるコンパイラもありますが、互換性のために/**/だけにしておきましょう。

Cの場合、/*から複数行にまたいで*/までコメントにする事ができるため、関数の先頭で、より詳しく「引数」「リターンコード」について記述することができます。また、そうしてほしいと思います。

・C++
C++では、Cのコメントが使える他に、//が使えます。JavaScriptやPHPでも//なので、私は最近めっきり//を使う機会が増えました。

コメントアウト

コメントアウトとは、普通の命令をコメント行にして無効化する事を言います。
$sql="insert into keijiban (onamae, message, passwd) values "
        ." ('$onamae', '$message', '$passwd');";
if (!pg_exec($handle, $sql)){
        die("insertに失敗しました");
}
//printf("sql=[%s]",$sql);
例えば、SQL文(insertやupdate)を生成する際に、SQLエラーが出てしまう原因がわからない時に、sql文の中を表示できると便利です。しかし、普段からsql文丸見えでは、セキュリティーもなにもあったもんじゃありませんので、役目の終わったSQL文の表示機能は隠します。

しかし、またいつどこでエラーが出るかわかりません。そこで、SQL文を表示する箇所は消さずにコメント行とします。プログラムが完成しても、またいつエラーが出るかわからないという不安な方は、ずっとコメントアウトしたまま残しておくと良いでしょう。

とりあえず多めに作っておく

仕様が曖昧なために、ある処理が必要かどうかわからない場合がります。例えば、入力処理において文字数チェックを入れるかどうか、また、使うことのできない半角記号の入力チェックとか、コードに数字以外があるとエラーにするかどうか、などです。

こういった場合、納期寸前になって「やっぱり必要だった」とか「○○のチェックがない。大至急修正してください」と言われた場合、納期寸前でパニックになっていたり、徹夜明けだったりして頭が働かず、大半が納期前に完成していたにもかかわらず、結局納期オーバーになってしまうケースがあります。

こういう時は、不要なルーチンでもとりあえず作っておきます。もしそれが不要だった場合、「○○の入力チェックが誤動作する」あるいは「○○のチェックは不要」と納期寸前に言われた場合にも、コメントアウトする事で簡単に対応できます。一般に、不足する箇所を作るより多すぎる箇所をコメントアウトする方がミスも少なく、徹夜明けの頭でも簡単に対応が可能なケースが多いです。

コメント文がわかるエディタを使う

これまで説明した通り、言語によってコメント文となる条件が異なります。また、言語によって拡張子が異なるケースがほとんどです。COBOLのソースなら.cbl、PHPのソースなら.php、Perlのソースなら.plなどです。

なので、エディタによっては拡張子で言語を判断し、拡張子ごとにコメント文を色分けしてくれるものがあります。筆者も愛用しているviviなどがそうです。コメント文は色分けされて表示されるエディタの方が、コメントアウトしている箇所がわかりやすく、コメントアウトし忘れや、誤ってコメントアウトしてしまった箇所を発見しやすくなります。

おわりに

コメントの大切さ、おわかりいただけたでしょうか。ここでおわかりいただけなかった方も、大トラブルにみまわれた時、もしコメントをたくさん入れてさえいれば、「あの時、入れておいてよかった」と心から思う時が来ることでしょう。
スポンサーリンク