C言語のコメンは奥が深い

2020/04/12

C言語 プログラミング 雑記 組み込み

組み込み系の方とお話しした際に、「C言語でコメントを書くときは基本的に /* */ を使い、// は使わない」と伺いました。

その時は詳しく理由を伺えなかったのですが、気になったので調べてまとめました。 もし間違っている箇所があればご教示いただけますと幸いです。

// コメントはもともとC++のコメント

私が学生時代にC言語を習ったときは、「コメントは2種類あり、1行コメントには // を使い、複数行コメントには /* */ を使う」と教わりました。 そのため、どちらもC言語のコメントだと思っていました。

しかし、下記の本の39ページに次のような記述がありました。

ヴイストン, ロボット実習教材研究会
オーム社 (2018-05-09)
売り上げランキング: 164,583

プログラム中に処理の内容をメモする場合は、コメントアウトという機能を使用します。 「/*」と「*/」で囲まれた文字列はコンパイル時に無視されるため、ここに任意の文字列を記述することができます。 この中には日本語や全角文字、予約語などを記述しても問題ありません。 見やすいソースを作成するためにも、積極的にコメントを残すことが望ましいです。

また、「//」もコメントアウトとして利用できます。 これはC++言語で採用されているコメントアウトですので、C言語では使用できない場合があります。 が、本書で利用しているLPCXpressoでは利用可能です。

なるほど、// コメントはもともとC++のコメントなのですね。 // コメントが使えない場合があるということで、どのような時に使えないのか調べてみると、古い規格では使えないようです。

最近の処理系では、Cモードでのコンパイル時に、// をコメントとして使えますが、ANSI Cの定義に組み込まれるようになったのはC99の規格からです(C89の規格では//は定義されていません)。よって、処理系によっては使えない場合があるのでCプログラミングでは注意しましょう(C++では大丈夫です)。

プログラミング演習ⅢC++ C言語との違いより引用
(C99の規格については、こちらのC99も参考にしてください。)

個人利用の場合は基本的に新しい規格でコーディングできるため、あまり気にする必要はありません。 しかし、組み込み系では開発環境によって古い規格でコーディングしなければならない場合があると思うので、確実に使える /* */ を使っているのだと思われます。

(2023/05/30 追記)

インターフェースの2023年7月号を読んでいたところ、組み込みシステムではC89(通称ANSI C)が今でも使われることがある、という記載がありました。 理由としては、組み込みシステムの製品寿命が長いことや、過去の資産との互換性などの問題があるためとのことです。

組み込みシステムではC99以前のC89, 通称ANSI Cが今でも使われていることがあります。 これは組み込みシステムの製品寿命が長いこともあり, 過去の資産との互換性などの問題から必要とされることがあるからです。 しかし, 今新たにRTOSを作成するにあたっては, よほど古いマイコンをターゲットにしない限りC99で問題ないでしょう。

(インターフェース 2023年7月号の34ページより引用)

組み込み系の開発者はC89を使う場合があるため、習慣的に /* */ コメントを使う場合が多いのかもしれません。

古い規格(C90)で // コメントが使えないか実験

gccには、規格を指定してコンパイルするオプションが存在します。

そのオプションを使って、古い規格でコンパイルエラーが起きることを確認します。

まず、test.c という名前で下記のファイルを作成します。

#include <stdio.h>

int main(void)
{
    // このコメントがあることでコンパイルエラーが起きるか確認
    printf("test\n");
    return 0;
}

そして、コマンドプロンプトで下記コマンドを入力します。

これを実行することで、// コメントが導入される前のC90規格でコンパイルできます。

gcc .\test.c -std=c90

実行結果は次の通りです。

.\test.c: In function 'main':
.\test.c:5:5: error: C++ style comments are not allowed in ISO C90
    5 |     // このコメントがあることでコンパイルエラーが起きるか確認
      |     ^
.\test.c:5:5: note: (this will be reported only once per input file)

エラーメッセージを見ると、「C++形式のコメントはISO C90ではサポートしていない」と出ています。

実行結果は載せませんが、// コメントが導入されたC99規格でも試しました。 実行したコマンドは下記の通りです。

gcc .\test.c -std=c99

特に問題なくコンパイルが通り、実行ファイルを実行すると test と表示されました。

// を使うと「ダメ文字」の影響を受ける場合がある

C言語のコメントについて調べていたところ、下記の記事がありました。

C言語の全角文字コメント行がコンパイルエラーを起こすことがある

この記事では、「このコメントを消すと動かないので、消してはいけない」というネタの仕組みを解説しています。

「このコメントがないとコンパイルエラーが起きる」という事象が発生する理由として、Shift-JIS特有の ダメ文字というものの影響があるそうです。

// コメントを使った場合、行末にバックスラッシュが存在すると、その行の改行が無視されてしまい、次の行もコメントアウトされてしまいます。

詳細は、こちらの記事を参照してください。
C言語の行コメント内にバックスラッシュ(¥)を入れると次の行がコメントアウトされることがある

私はエディタとしてVS Codeを使用しているのですが、末尾にバックスラッシュが入っていると、次の行がコメントアウトされていることが視覚的に分かります。 コメントアウトされている行が緑色になっており、for文のところまで意図せずにコメントアウトされていることが分かります。

末尾にバックスラッシュが入ったコメントの次の行もコメントアウトされているため、「変数 i が宣言されていない」というエラーと、for文の } がmain関数の } と解釈され、} が一つ余っているというエラーが出ています。

Shift-JISでは、漢字などの多くの全角文字が2バイトで表現されます。 その2バイト目がバックスラッシュと同じ文字コード(0x5C)になっている文字があり、このような文字を ダメ文字といいます。

このダメ文字が // コメントの末尾に入っていると、末尾にバックスラッシュが入っていると認識されてしまい、次の行がコメントアウトされてしまう場合があります。

具体的には、下記のようなものがあるようです。この中で末尾によく使われるのは「能」や「表」あたりでしょうか。

ソ噂浬欺圭構蚕十申曾箪貼能表暴予禄兔喀媾彌拿杤歃濬畚秉綵臀藹觸軆鐔饅鷭

だめ文字より引用

この現象は // コメントを使ったときに発生するものなので、/* */ コメントを使えば回避できます。 ダメ文字はバグの原因になり得るため、それを回避するために組み込み系の開発者は /* */ コメントを使っていると考えられます。

2種類のコメントでダメ文字を使った場合の動作確認

///* */ のコメントの違いを、簡単なコードを書いて実験してみます。

最初に、// を使った場合の挙動を確認します。

#include <stdio.h>

int main(void)
{
    // このコメントがあると下の文字を表示することが不可能
    printf("test\n");
    printf("abc\n");
    return 0;
}

上記コードは、コメントの末尾にダメ文字である「能」を使っています。

Shift-JIS形式でこのコードを保存して実行した結果がこちらです。

abc

ダメ文字を使ったコメントの直下の printf 文がコメントアウトされてしまったため、test という文字列が表示されていません。

次に、/* */ を使った場合の挙動です。

#include <stdio.h>

int main(void)
{
    /* このコメントがあっても下の文字を表示することが可能 */
    printf("test\n");
    printf("abc\n");
    return 0;
}
test
abc

こちらもコメントにダメ文字の「能」を使っていますが、きちんと処理が実行されています。

ここで、参考文献にもあった「コメントを消すと動作しなくなる例」を再現してみます。

簡単なコードですが、1から10までカウントするコードです。こちらをShift-JIS形式で保存して実行してみます。

#include <stdio.h>

int main(void)
{
    int i;

    // この処理があることで1から10まで数えることが可能
    for (i = 1; i <= 10; i++) {
        printf("%d\n", i);
    }

    return 0;
}

このコードを実行すると、下記のようなエラーが出力されます。

"message": "expected identifier or '(' before '}' token",

これは、} の前に対応する { がないと認識されているために出ているエラーです。

ぱっと見では { があるため問題ないコードに見えますが、コメントの最後に「能」というダメ文字があり、for文の最初がコメントアウトされてしまいます。

そのため、ダメ文字があるコメントの次の行に別のコメントを書くことで、処理に関係ないコメントがコメントアウトされるようになり、コンパイルエラーを防ぐことができます。

補足ですが、下記のようにコメントを挿入することでコンパイルエラーを回避できます。

#include <stdio.h>

int main(void)
{
    int i;

    // この処理があることで1から10まで数えることが可能
    // このコメントは消さないでください。消すとバグります
    for (i = 1; i <= 10; i++) {
        printf("%d\n", i);
    }

    return 0;
}

補足:Pythonの場合はどうなるか

私は普段Pythonを使っているので、Pythonの場合はどうなるか試してみました。

Pythonの場合、1行コメントは # です。

先ほどと同様に、test.py という名前で下記のコードをShift-JIS形式で保存し、実行します。

# このコメントがあると下の文字を表示することが不可能
print("abc")
print("test")

実行した結果、下記のようなエラーメッセージが表示されました。

SyntaxError: Non-UTF-8 code starting with '\x89' in file .\test.py on line 1,
but no encoding declared; see https://python.org/dev/peps/pep-0263/ for details

Pythonは、デフォルトではShift-JISで記述されたソースコードを実行できないようです。 下記のように1行目に文字コードを指定すると、Shift-JISで記述されたコードでも実行できるようになります。

# coding: shift_jis
# このコメントがあると下の文字を表示することが不可能
print("abc")
print("test")

実行結果は下記の通りでした。

abc
test

Pythonの場合、Shift-JISで記述されていてもダメ文字の影響を受けないことが分かります。

参考:ダメ文字が影響していると思われる不具合例

最近出たゲームで、キャラクター名にカタカナの「ソ」が入るとバグるというものがあったようです。 先ほどダメ文字の一覧で紹介した通り、「ソ」はダメ文字の一つです。

今回の記事で紹介したものは、コメントの末尾にダメ文字があると意図せず次の行がコメントアウトされてしまうという事象でした。 一方で、プログラムの実装によっては、途中にダメ文字が入ってもバグることがあるようです。

(2026/07/05 追記)コメントとコメントアウトの違い

Xを見ていたら、「コメント」と「コメントアウト」は違うという議論がなされていました。 この記事を見返したところ、タイトルの「コメントアウト」という言葉を誤った意味で使用していたため修正しました。(あまり違いを意識せずに使用していたのでよくなかったと思います。)

コメントとコメントアウトの定義の違いについては、下記記事が分かりやすかったので紹介します。 皆様は私のように間違えないよう注意してください。

「コメント」と「コメントアウト」の違い

まとめ

たかがコメントと思っていましたが、調べてみると奥が深かったです。

調べた結果、組み込み系の開発者が /* */ コメントを使う理由は、主に下記の2つだと思います。

  1. 古い規格の場合、// が使えないため、どの規格でも対応できる /* */ を使用している。
  2. Shift-JIS特有のダメ文字によるバグを回避できる。

もし他にも理由があれば教えていただけますと幸いです。

自己紹介

はじめまして 社会人になってからバイクやプログラミングなどを始めました。 プログラミングや整備の記事を書いていますが、独学なので間違った情報が多いかもしれません。 間違っている情報や改善点がありましたらコメントしていただけると幸いです。

X(旧Twitter)

フォローお願いします!

ラベル

QooQ