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

2020/04/12

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

組み込み系の方とお話をした際に、C言語で記述する際のコメントアウトは基本/**/で
//を使わないというお話をお伺いしました。

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

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

私が学生時代に授業でC言語を習ったときはコメントアウトは2つあり、1行をコメントアウトする際は//を使い、複数行コメントアウトをする場合は/**/を使うと授業で習いました。このようにC言語の授業で習ったためどちらのコメントアウトもC言語のコメントアウトだと思っておりました。

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

ARMマイコンによる組込みプログラミング入門 改訂2版: ロボットで学ぶC言語
ヴイストン, ロボット実習教材研究会
オーム社 (2018-05-09)
売り上げランキング: 164,583

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

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

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

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

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

C89を使う場合があるという事で、習慣的に/**/のコメントを使う場合が多いのかもしれないです。

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

gccには規格を指定してコンパイルをするオプションが存在します。
そちらを使って古い規格でコンパイルエラーが起きることを確認します。
まずtest.cという名前で下記のファイルを作成します。
#include stdio .h
void main(){
	//このコメントがあることでコンパイルエラーが起きるか確認
	printf("test\n");
}

そしてコマンドプロンプトにて下記コマンドを入力します。
こちらを実行することでC90という//のコメントアウトが導入される前の規格でコンパイルを行えます。
gcc .\test.c -std=c90

実行結果は次の通りです。
.\test.c: In function 'main':
.\test.c:3:5: error: C++ style comments are not allowed in ISO C90
    3 |     //
      |     ^
.\test.c:3:5: note: (this will be reported only once per input file)

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

実行結果は載せないですが、//のコメントアウトが導入されたC99の規格も試しました。
実行したコマンドは下記のとおりです。
gcc .\test.c -std=c99

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

//を使うと"ダメ文字"の影響を受けてしまう場合がある。

C言語のコメントについて調べていたら下記のような記事がありました。

この記事がなかなか面白く、「このコメントを消すと動かないので、消しちゃダメ」というネタの仕組みを解説してくれています。

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

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

私はエディタとしてVScodeを使用しているのですが、末尾にバックスラッシュが入っていると次の行がコメントアウトされているのが視覚的に分かります。

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

Shift-JISでは1文字当たり2バイトで表現されるのですが、2バイト目がバックスラッシュと同じ文字コード(0x5c)となっている文字があり、このような文字をダメ文字といいます。

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

具体的には下記のようなものがあるみたいです。(この中で末尾によく使われるのは"能"とか"表"とかですかね?)
ソ噂浬欺圭構蚕十申曾箪貼能表暴予禄兔喀媾彌拿杤歃濬畚秉綵臀藹觸軆鐔饅鷭

 だめ文字より引用


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

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

//と/**/のコメントアウトの違いを簡単なコードを書いて実験してみます。
最初に//を使った場合の挙動を確認します。
#include <stdio.h>
void main(){
	//このコメントがあると下の文字を表示することが不可能
	printf("test\n");
	printf("abc\n");
}
上記コードはコメントの末尾にダメ文字である"能"を使っています。
Shift-JIS形式でこのコードを保存して実行した結果がこちらです。
abc

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

次に/**/を使った場合の挙動です。
#include <stdio.h>
void main(){
	/*このコメントがあっても下の文字を表示することが可能*/
	printf("test\n");
	printf("abc\n");
}

test
abc

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

ここで参考文献にもあったコメントを消すと動作しなくなる例を再現してみます。
簡単なコードですが、1から10までカウントするコードです。こちらをShift-jis形式で保存して実行してみます。

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

このコードを実行すると下記のようなエラーが出力されます。
"message": "expected identifier or '(' before '}' token",
これは"}"の前に"{"が無いと認識されてしまい、出ているエラーです。

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

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

補足ですが下記のようにコメントを挿入することでコンパイルエラーが回避できます。
#include <stdio.h>
void main(){
	//この処理があることで1から10まで数えることが可能
	//このコメントは消さないで、消すとバグる
	for(int i=1;i<=10;i++){
		printf("%d \n",i);
		
	}
}

(補足)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 http://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で記述されてもダメ文字の影響を受けないようになってることがわかります。

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

最近出たゲームでキャラクターの名前にカタカナの"ソ"が入るとバグるというものがあったみたいです。



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

まとめ

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

調べた結果、組み込み系の人がコメントアウトで/**/を使ってい理由は下記2つだと思います。
①古い規格の場合、//が使えないのでどんな規格でも対応できる/**/を使用している。
②ダメ文字によるバグを回避することができる。

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

自己紹介

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

X(旧Twitter)

フォローお願いします!

ラベル

QooQ