第78章 逃げの一手

 printf を使うとき「\n が改行になる」といいました。他にも、何も説明しませんでしたが strcpy_ex(第49章参照)のところでタブを \t と扱っていました。この \ って何なんでしょうか? 今回はその問題に迫ってみたいと思います。


 では、今回の要点です。


 では、いってみましょう。


 さて、改行の文字コードを取得してみましょう。

int letter = '
';

 さて、バックスペースの文字コードを取得してみましょう。

int letter = ';

 さて、シングルクオーテーション (') の文字コードを...って、もういいですね。このように、「改行」「バックスペース」「シングルクオーテーション」などの文字コードは確かに存在するのですが、いざそれをソース上で表現しようとしても問題があります。改行やシングルクオーテーションはともかく、バックスペースに至っては何の事やら分かりません。

 こういうときのために、C/C++ではエスケープシーケンスというものを利用します。エスケープシーケンス(逃げ道)です。

 例えば、改行は '\n' 、バックスペースは '\b' 、シングルクオーテーションは '\'' という風に、エスケープシーケンスは \ から始まる特殊な表記になります。

 エスケープシーケンスを表現するために \ を使いました。では、\ の文字コードを取得したいときはどうすればいいのでしょうか?

int letter = '\';

 おっと、これでは \' がシングルクオーテーションを表すエスケープシーケンスになってしまい、おかしくなってしまいます。\ は別に何の変哲もない普通の文字なのですが、このせいで \ もエスケープシーケンスを使って表現しなくてはいけないのです。

 \ を表すエスケープシーケンスは \\ です。簡単ですね。

int letter = '\\';

 また、文字コードを直接使用することもできます。例えば文字コード48(16進数で30)を表現したいときは

int letter = '\x30';

と、\x に2桁までの16進数を続けます。これは文字コードとして有効な値以外、例えば255(16進数でFF)などでも使えます。

 他にもいろいろなエスケープシーケンスがあります。ちょっと表にしてみましょう。

表現 意味
\a 警告音(ビープ音)
\b バックスペース
\f 改ページ(※1)
\n 改行(※2)
\r キャリッジリターン(※2)
\t 水平タブ
\v 垂直タブ(※1)
\? 半角クエスチョンマーク(※3)
\' 半角シングルクオーテーション
\" 半角ダブルクオーテーション
\\ 半角円マーク(バックスラッシュ)
\ooo 8進数(3桁までの8進数を ooo に書く)(※4)
\xhh 16進数(2桁までの16進数を hh に書く)(※5)

※1:プリンタ出力用の制御文字です。

※2:OSによって意味が変わってきます。

※3:「3文字表記」と区別するために必要になります。3文字表記についてはここでは話しませんが、? を2つ以上連続して書きたいときには \? を使うようにして下さい。

※4:なるべく3桁書くようにして下さい。こういう癖をつけておけば、この後に数字が続くときにおこるバグを防げます。

※5:先頭の0は桁数に含みません。文字列中で例えば数値の10(16進数で a )と小文字の b を連ねたものを表現したいときは、"\xab" とはできず、"\x0ab" ともできません。第58章で紹介した方法、"\xa""b"を使えば問題ありません。




 さて、ここで注意することがあります。このエスケープシーケンスは「特殊な文字コードをソース上で表すための手段」に過ぎません。コンパイルされると対応する文字コードに変換されるのです。

 分かっている人にとってはくどいでしょうが、プログラムを組んでこのことを確認してみましょう。

プログラム実行結果
// Escape1.cpp
#include <iostream.h>
#include <stdio.h>

void DumpLetterCode(const char* str)
{
    for(; *str; str++)
        printf("%02X ", (unsigned char)*str);
    cout << endl;
}

int main()
{
    DumpLetterCode("\"\'\?\\");
    DumpLetterCode("\a\b\t\n\v\f\r");
    DumpLetterCode("\xab" "\x0ab" "\xa" "b");
    DumpLetterCode("\11111");
    DumpLetterCode("c:\new\abc");
    DumpLetterCode("c:\\new\\abc");

    return 0;
}
22 27 3F 5C 
07 08 09 0A 0B 0C 0D 
AB AB 0A 62 
49 31 31 
63 3A 0A 65 77 07 62 63 
63 3A 5C 6E 65 77 5C 61 62 63 

 最初は "\"\'\?\\" です。これは ", ', ?, \ の4つの文字コードを連ねた文字列になるはずです。それぞれ文字コードは16進数で22,27,3F,5Cなのですが...ちゃんとそうなっているみたいですね。

 見た目は5C,22,5C,27,5C,3F,5C,5Cに見えるのですが、それはソース上だけの話です。コンパイルされてしまえば、それぞれがエスケープシーケンスに対応する文字コード1つになってしまいます。


 次は特殊な文字コードを集めた "\a\b\f\n\r\t\v" です。このような特殊な機能を持った特殊な文字のことを制御文字といいます。これらの文字コードは...07〜0Dになっていますね。そうなるように並べましたから。

 これも、見た目は5C,61,5C,62,... に見えるのですが、それはソース上だけの話だということがよく分かるでしょう。


 次は上の表で※5に書いてあることを実践したまでです。分かりやすいように "\xab" "\x0ab" "\xa" "b" と分けて書いてあります。

 ※5に書いてあるとおりなら、b の文字コードは62なのでAB,AB,0A,62になるはずです。実際にも...そうなっていますね。


 次はよくあるバグの1つです。慣れていても、たまにやってしまうバグです。

 "c:\new\abc" というのはあるフォルダのパス名です(「パス」とはファイルやフォルダの位置のことです)。各フォルダ名を \ で区切るというのはWindowsではよくやることですが、ソース上では \ はエスケープシーケンスの印になってしまいます。普通はエラーになるのですが、ここではたまたま \n も \a も意味を持っています。なので、エラーは出ません。

 実行結果を見て下さい。\n のところは0Aに、\a のところは07になってしまっています。2番目の結果と照らし合わしてみると、\n と \a になってしまっていることは一目瞭然です。

 なので、ここは正しくは次のように "c:\\new\\abc" とすべきなのです。こうすると、きちんと \\n は5C,6Eに、\\a は5C,61になってくれていますね。

 パス名では \ を \\ にし忘れることがよくあるので気を付けましょう。

 そして、コンパイル後でも \\ と、つまり5C,5Cとなっていると勘違いする人も多いです。エスケープシーケンスはあくまでソース上で表現できないものを表現するための逃げ道なのであって、メモリ上の表現までこうなっているのではないのです。


 それでは、今回の要点です。


 もう今回で基本的な事項は全て話してしまいました。まだまだいろいろ話したいこともあるのですが、ちょっと重箱の隅をつつくような感じになってしまいます。そういったことは第3部で行いたいと思います。お楽しみに。

 残りあと2章は、言語の話ではなく、その他で重要なことについての話や、次の部のイントロダクションのようなものにしたいと思っています。

 それでは、次回まで。


第77章 リテラル文字列 | 第79章 Significance of Errors

Last update was done on 2001.6.16

この講座の著作権はロベールが保有しています