コメントとは、コンパイラが捨ててしまうテキストです。あとからコードを読む人のためだけに存在しており、その一人はたいていあなた自身です。C言語には 2 つの形があり、どちらが適切かを知るのに 2 分もかかりません。
2 つの形
実行してみてください。出力は 1 行です。どちらのコメントも、コンパイラがプログラムを解析する前に削除されています。実行時のコストはゼロで、実行ファイルにも何も加えません。
// は物理的な行末まで続きます。その行でその後ろに何かを書くことはできないので、次のコードは見た目どおりには動きません:
int x = 5; // set x to five int y = 6; /* y is never declared */
/* ... */ は、どこにあろうと最初の */ で終わります。行の途中で始めて途中で終えることもでき、それがたまに役立ちます:
int total = price /* before tax */ + shipping;
なぜ 2 つの記法があるのか
/* */ は 1972 年からある、もともとの C言語のものです。// は C++ から来たもので、C言語に正式に加わったのは C99 です。この歴史は、古いコードを読むと気づくあることを説明してくれます。C89 への移植性を持たせて書かれたライブラリは、1 行のコメントにも /* */ を使っています。当時まだサポートしていた古いツールチェーンでは // がコンパイルできなかったからです。
今日、あなたが使うであろうコンパイラはどれも両方を受け付けます。普通の注記には // を、コメントが本当に複数行にまたがるときには /* */ を使いましょう。とても古い組み込み向けコンパイラを対象にしているなら、// に頼る前に確認してください。
コメントはネストできない
これが唯一の本物の罠です:
/* Disable this section for now
int a = compute();
/* the classic helper - keep an eye on it */
int b = a * 2;
*/
ブロックコメントは最初の */、つまり 3 行目のもので終わります。すると 4 行目と 5 行目はふたたび生きたコードになり、6 行目の末尾の */ は構文エラーになります。コンパイラのメッセージは最後の行を指しますが、原因についてはまったく助けになりません。
直し方は、代わりにプリプロセッサを使うことです。こちらはネストをきちんと扱います:
#if 0
int a = compute();
/* the classic helper - keep an eye on it */
int b = a * 2;
#endif
#if 0 は決して真にならないので、プリプロセッサはコンパイラがそれを見る前に #endif までをすべて削除します。中のコメントや引用符、さらに別の #if ブロックがあっても壊れませんし、片づけるときに検索しやすいのも利点です。
デバッグ中のコードのコメントアウト
行を一時的に取り除くことは、コメントの最も日常的な使い道です。プログラムがおかしな動きをするとき、1 文ずつ無効にしていけば、どれが効いているのかが分かります。
printf のコメントを外してもう一度実行し、ループが答えを積み上げていく様子を見てみましょう。表示による追跡は洗練された方法ではありませんが、C言語では速く、いつでも確実に使えます。デバッガはもっと多くを教えてくれますが、printf はその場で何かを教えてくれます。
これが散らかった状態にならないための習慣が 2 つあります。コメントアウトしたコードは、コミットする前に削除すること。バージョン管理が古い版を覚えていてくれるので、あなたが覚えておく必要はありません。そして、無効にした行を意図的に残すときは、その理由を隣に書き添えることです。
ドキュメント用のコメント
関数の上のブロックコメントは、その関数が何をするのか、引数が何を意味するのか、そして意外な点があれば何なのかを説明する場所です。
Doxygen のようなツールは、こうした構造化されたコメントを読んでリファレンス文書を生成します。Doxygen 自身のスタイルは /** ... */ に @param と @return のタグを使います:
/**
* Converts Celsius to Fahrenheit.
* @param c temperature in Celsius
* @return the same temperature in Fahrenheit
*/
double celsius_to_fahrenheit(double c);
自分のコードならどちらでも構いません。大切なのは、コメントが人の読む宣言の隣に置かれていること - たいていはヘッダーファイルです - であって、実装の中に埋もれていないことです。
コメントする価値があるもの
実際のコードベースに触れても生き残る規則はこれです。何をしているかではなく、なぜそうするかをコメントせよ。
i++; // increment i <- says nothing the code did not
/* Skip the BOM: files exported by the old system start with
three bytes that are not part of the data. */
offset += 3;
2 つ目のコメントには、コードのどこにも書かれていない情報が入っています。1 つ目はノイズであり、いずれ説明しているはずの行と食い違います。コードが変わってもコメントは更新されないからです。
C言語で特にコメントする価値が本当にあるもの:
- このメモリを誰が所有しているか。 関数が、呼び出し側が
freeしなければならないポインタを返すなら、そう書きましょう。C言語にはそれを型で表す手立てがありません。 - 単位と範囲。
int timeout;は曖昧です。秒でしょうか、ミリ秒でしょうか。 - 自明でない正しさ。 なぜループが
n - 1で止まるのか、なぜこのキャストが安全なのか、なぜバッファが 256 バイトなのか。 - 意図的な奇妙さ。 バグに見えるがバグではないコードは、その旨を書いておかないと、後の読み手から「修正」を引き寄せます。
このコメントは価値を果たしています。その下の行は冗長に見えますが、そうではないからです。
文字列の中のコメントはコメントではない
最後にもう 1 つ。コメントの記号は、文字列リテラルや文字定数の中では特別な意味を持ちません:
どちらの行もそのまま表示されます。コンパイラはコメントを探す前に文字列をトークン化するので、引用符の中の // は単なる 2 文字です。(1 行目の %% は、printf でパーセント記号そのものを表示する方法です。% 1 つでは書式指定子が始まってしまいます。)
よくある質問
C言語でコメントを書くには?
2 通りあります。// これはコメントです は行末まで続きます。/* これはコメントです */ は何行にもまたがることができ、閉じる */ で終わります。どちらもコンパイル前に取り除かれるので、プログラムに影響することはありません。
C言語は // コメントに対応していますか?
C99 以降は対応しています。C++ から借りてきたもので、今日ではどこでも使えます。拒否するのは本当に古い C89 のコンパイラだけで、だからこそとても古いコードは 1 行のコメントにも /* */ を使っています。
C言語でコメントをネストできますか?
できません。/* outer /* inner */ still outer */ は最初の */ で終わるので、still outer */ が壊れたコードとして残ります。すでに /* */ コメントを含むブロックを無効化したいなら、代わりに #if 0 ... #endif を使いましょう。こちらは正しくネストします。
C言語でコードのブロックをコメントアウトするには?
中にブロックコメントがなければ /* */ で囲むか、各行の先頭に // を付けます。広い範囲に対して確実なのは、前に #if 0、後ろに #endif を置く方法です。プリプロセッサがその間をすべて取り除き、中にコメントや引用符があっても壊れません。